diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2022-11-03 10:02:44 +0100 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2022-11-03 10:02:44 +0100 |
commit | 1c380bfeff1e13a9f7d506460336659502ca052d (patch) | |
tree | 48081d47748d4563eeaa76662287eb19638c8591 /src | |
parent | 4e774db12d5ebe7a20eded6dd434a289e27999e5 (diff) |
New upstream version 1.1.0+dfsg
Diffstat (limited to 'src')
575 files changed, 4986 insertions, 3001 deletions
diff --git a/src/silx.egg-info/PKG-INFO b/src/silx.egg-info/PKG-INFO new file mode 100644 index 0000000..f953bc6 --- /dev/null +++ b/src/silx.egg-info/PKG-INFO @@ -0,0 +1,148 @@ +Metadata-Version: 2.1 +Name: silx +Version: 1.1.0 +Summary: Software library for X-ray data analysis +Home-page: http://www.silx.org/ +Author: data analysis unit +Author-email: silx@esrf.fr +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Environment :: Win32 (MS Windows) +Classifier: Environment :: X11 Applications :: Qt +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Scientific/Engineering :: Physics +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.5 +Provides-Extra: full +Provides-Extra: test +License-File: LICENSE + + +silx toolkit +============ + +.. |silxView| image:: http://www.silx.org/doc/silx/img/silx-view-v1-0.gif + :height: 480px + +The purpose of the *silx* project is to provide a collection of Python packages to support the +development of data assessment, reduction and analysis applications at synchrotron +radiation facilities. +*silx* aims to provide reading/writing tools for different file formats, data reduction routines +and a set of Qt widgets to browse and visualise data. + +The current version features: + +* Support of `HDF5 <https://www.hdfgroup.org/HDF5/>`_, + `SPEC <https://certif.com/spec.html>`_ and + `FabIO <http://www.silx.org/doc/fabio/dev/getting_started.html#list-of-file-formats-that-fabio-can-read-and-write>`_ + images file formats. +* OpenCL-based data processing: image alignment (SIFT), + image processing (median filter, histogram), + filtered backprojection for tomography, + convolution +* Data reduction: histogramming, fitting, median filter +* A set of Qt widgets, including: + + * 1D and 2D visualization widgets with a set of associated tools using multiple backends (matplotlib or OpenGL) + * OpenGL-based widgets to visualize data in 3D (scalar field with isosurface and cut plane, scatter plot) + * a unified browser for HDF5, SPEC and image file formats supporting inspection and + visualization of n-dimensional datasets. + +* a set of applications: + + * a unified viewer (*silx view filename*) for HDF5, SPEC and image file formats + + |silxView| + + * a unified converter to HDF5 format (*silx convert filename*) + + +Installation +------------ + +To install silx (and all its dependencies), run: + +.. code-block:: bash + + pip install silx[full] + +To install silx with a minimal set of dependencies, run: + +.. code-block:: bash + + pip install silx + +Or using Anaconda on Linux and MacOS: + +.. code-block:: bash + + conda install silx -c conda-forge + +Unofficial packages for different distributions are available: + +- Unofficial Debian10 and Ubuntu20.04 packages are available at http://www.silx.org/pub/linux-repo/ +- CentOS 7 rpm packages are provided by Max IV at: http://pubrepo.maxiv.lu.se/rpm/el7/x86_64/ +- Fedora 23 rpm packages are provided by Max IV at http://pubrepo.maxiv.lu.se/rpm/fc23/x86_64/ +- Arch Linux (AUR) packages are also available: https://aur.archlinux.org/packages/python-silx + +`Detailed installation instructions <http://www.silx.org/doc/silx/dev/install.html>`_ +are available in the documentation. + +Documentation +------------- + +The documentation of `latest release <http://www.silx.org/doc/silx/latest/>`_ and +the documentation of `the nightly build <http://www.silx.org/doc/silx/dev>`_ are +available at http://www.silx.org/doc/silx/ + +Testing +------- + +*silx* features a comprehensive test-suite used in continuous integration for +all major operating systems: + +- Github Actions CI status: |Github Actions Status| +- Appveyor CI status: |Appveyor Status| + +Please refer to the `documentation on testing <http://www.silx.org/doc/silx/dev/install.html#testing>`_ +for details. + +Examples +-------- + +Some examples of sample code using silx are provided with the +`silx documentation <http://www.silx.org/doc/silx/dev/sample_code/index.html>`_. + + +License +------- + +The source code of *silx* is licensed under the MIT license. +See the `LICENSE <https://github.com/silx-kit/silx/blob/master/LICENSE>`_ and +`copyright <https://github.com/silx-kit/silx/blob/master/copyright>`_ files for details. + +Citation +-------- + +*silx* releases can be cited via their DOI on Zenodo: |zenodo DOI| + +.. |Github Actions Status| image:: https://github.com/silx-kit/silx/workflows/CI/badge.svg + :target: https://github.com/silx-kit/silx/actions +.. |Appveyor Status| image:: https://ci.appveyor.com/api/projects/status/qgox9ei0wxwfagrb/branch/master?svg=true + :target: https://ci.appveyor.com/project/ESRF/silx?branch=master +.. |zenodo DOI| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.591709.svg + :target: https://doi.org/10.5281/zenodo.591709 + + diff --git a/src/silx.egg-info/SOURCES.txt b/src/silx.egg-info/SOURCES.txt new file mode 100644 index 0000000..6d81290 --- /dev/null +++ b/src/silx.egg-info/SOURCES.txt @@ -0,0 +1,1522 @@ +CHANGELOG.rst +LICENSE +MANIFEST.in +README.rst +build-deb.sh +copyright +pyproject.toml +requirements-dev.txt +requirements.txt +run_tests.py +setup.py +stdeb.cfg +doc/source/changelog.rst +doc/source/conf.py +doc/source/index.rst +doc/source/install.rst +doc/source/license.rst +doc/source/overview.rst +doc/source/troubleshooting.rst +doc/source/tutorials.rst +doc/source/virtualenv.rst +doc/source/Tutorials/Image.ipynb +doc/source/Tutorials/array_widget.rst +doc/source/Tutorials/convert.rst +doc/source/Tutorials/fit.rst +doc/source/Tutorials/fitconfig.rst +doc/source/Tutorials/io.rst +doc/source/Tutorials/specfile_to_hdf5.rst +doc/source/Tutorials/writing_NXdata.rst +doc/source/Tutorials/Sift/sift.ipynb +doc/source/Tutorials/img/arraywidget3D_0.png +doc/source/Tutorials/img/arraywidget3D_1.png +doc/source/Tutorials/img/arraywidget5D_0.png +doc/source/Tutorials/img/arraywidget5D_1.png +doc/source/Tutorials/img/custom_config_scale0.5.png +doc/source/Tutorials/img/custom_config_scale1.0.png +doc/source/Tutorials/img/custom_config_scale2.1.png +doc/source/Tutorials/img/fitwidget1.png +doc/source/Tutorials/img/fitwidget2.png +doc/source/Tutorials/img/fitwidget3.png +doc/source/Tutorials/img/fitwidget4.png +doc/source/Tutorials/img/fitwidget5.png +doc/source/Tutorials/img/silx_view_edf.png +doc/source/Tutorials/img/stripbg_plot1.png +doc/source/Tutorials/img/stripbg_plot2.png +doc/source/applications/convert.rst +doc/source/applications/index.rst +doc/source/applications/view.rst +doc/source/applications/img/silx-view-hdf5.png +doc/source/applications/img/silx-view-image.png +doc/source/applications/img/silx-view-table.png +doc/source/description/index.rst +doc/source/description/sift.rst +doc/source/description/img/sift_bench_cpu0.png +doc/source/description/img/sift_bench_cpu_kp.png +doc/source/description/img/sift_bench_cpu_res.png +doc/source/description/img/sift_bench_gpu0.png +doc/source/description/img/sift_bench_gpu_kp.png +doc/source/description/img/sift_bench_gpu_res.png +doc/source/description/img/sift_dog1.png +doc/source/description/img/sift_frame_ROI.png +doc/source/description/img/sift_match1.png +doc/source/description/img/sift_match2.png +doc/source/description/img/sift_orientation.png +doc/source/ext/snapshotqt_directive.py +doc/source/ext/sphinxext-archive.py +doc/source/img/silx.ico +doc/source/img/silx_large.png +doc/source/img/silx_small.png +doc/source/modules/index.rst +doc/source/modules/resources.rst +doc/source/modules/sx.rst +doc/source/modules/gui/colors.rst +doc/source/modules/gui/console.rst +doc/source/modules/gui/designer.rst +doc/source/modules/gui/gallery.rst +doc/source/modules/gui/icons.rst +doc/source/modules/gui/index.rst +doc/source/modules/gui/qt.rst +doc/source/modules/gui/utils.rst +doc/source/modules/gui/data/arraytable.rst +doc/source/modules/gui/data/dataviewer.rst +doc/source/modules/gui/data/dataviewerframe.rst +doc/source/modules/gui/data/index.rst +doc/source/modules/gui/data/numpyaxesselector.rst +doc/source/modules/gui/data/textformatter.rst +doc/source/modules/gui/data/img/ArrayTableWidget.png +doc/source/modules/gui/data/img/DataViewer.png +doc/source/modules/gui/data/img/DataViewerFrame.png +doc/source/modules/gui/data/img/NumpyAxesSelector.png +doc/source/modules/gui/dialog/abstractdatafiledialog.rst +doc/source/modules/gui/dialog/colormapdialog.rst +doc/source/modules/gui/dialog/datafiledialog.rst +doc/source/modules/gui/dialog/datasetdialog.rst +doc/source/modules/gui/dialog/groupdialog.rst +doc/source/modules/gui/dialog/imagefiledialog.rst +doc/source/modules/gui/dialog/index.rst +doc/source/modules/gui/dialog/img/abstractdatafiledialog.png +doc/source/modules/gui/dialog/img/colormapdialog.png +doc/source/modules/gui/dialog/img/datafiledialog.png +doc/source/modules/gui/dialog/img/datasetdialog.png +doc/source/modules/gui/dialog/img/groupdialog.png +doc/source/modules/gui/dialog/img/imagefiledialog_edf.png +doc/source/modules/gui/dialog/img/imagefiledialog_h5.png +doc/source/modules/gui/fit/backgroundwidget.rst +doc/source/modules/gui/fit/fitwidget.rst +doc/source/modules/gui/fit/index.rst +doc/source/modules/gui/fit/img/BackgroundDialog.png +doc/source/modules/gui/fit/img/FitWidget.png +doc/source/modules/gui/hdf5/examples_hdf5widget.rst +doc/source/modules/gui/hdf5/getting_started.rst +doc/source/modules/gui/hdf5/h5node.rst +doc/source/modules/gui/hdf5/hdf5contextmenuevent.rst +doc/source/modules/gui/hdf5/hdf5headerview.rst +doc/source/modules/gui/hdf5/hdf5treemodel.rst +doc/source/modules/gui/hdf5/hdf5treeview.rst +doc/source/modules/gui/hdf5/index.rst +doc/source/modules/gui/hdf5/nexussortfilterproxymodel.rst +doc/source/modules/gui/hdf5/img/Hdf5Example.png +doc/source/modules/gui/hdf5/img/Hdf5TreeView.png +doc/source/modules/gui/img/IPythonDockWidget.png +doc/source/modules/gui/img/IPythonWidget.png +doc/source/modules/gui/plot/compareimages.rst +doc/source/modules/gui/plot/compleximageview.rst +doc/source/modules/gui/plot/dev.rst +doc/source/modules/gui/plot/getting_started.rst +doc/source/modules/gui/plot/imageview.rst +doc/source/modules/gui/plot/index.rst +doc/source/modules/gui/plot/items.rst +doc/source/modules/gui/plot/plotsignal.rst +doc/source/modules/gui/plot/plottoolbuttons.rst +doc/source/modules/gui/plot/plotwidget.rst +doc/source/modules/gui/plot/plotwindow.rst +doc/source/modules/gui/plot/printpreviewtoolbutton.rst +doc/source/modules/gui/plot/profile.rst +doc/source/modules/gui/plot/roi.rst +doc/source/modules/gui/plot/roistatswidget.rst +doc/source/modules/gui/plot/scatterview.rst +doc/source/modules/gui/plot/stackview.rst +doc/source/modules/gui/plot/statswidget.rst +doc/source/modules/gui/plot/utils.rst +doc/source/modules/gui/plot/actions/control.rst +doc/source/modules/gui/plot/actions/examples.rst +doc/source/modules/gui/plot/actions/fit.rst +doc/source/modules/gui/plot/actions/histogram.rst +doc/source/modules/gui/plot/actions/index.rst +doc/source/modules/gui/plot/actions/io.rst +doc/source/modules/gui/plot/actions/medfilt.rst +doc/source/modules/gui/plot/actions/img/fftAction0.png +doc/source/modules/gui/plot/actions/img/fftAction1.png +doc/source/modules/gui/plot/actions/img/shiftAction0.png +doc/source/modules/gui/plot/actions/img/shiftAction3.png +doc/source/modules/gui/plot/img/BasicGridStatsWidget.png +doc/source/modules/gui/plot/img/BasicStatsWidget.png +doc/source/modules/gui/plot/img/CompareImages.png +doc/source/modules/gui/plot/img/ComplexImageView.png +doc/source/modules/gui/plot/img/ImageView.png +doc/source/modules/gui/plot/img/LimitsToolBar.png +doc/source/modules/gui/plot/img/Plot1D.png +doc/source/modules/gui/plot/img/Plot2D.png +doc/source/modules/gui/plot/img/PlotWidget.png +doc/source/modules/gui/plot/img/PlotWindow.png +doc/source/modules/gui/plot/img/PositionInfo.png +doc/source/modules/gui/plot/img/ROIStatsWidget.png +doc/source/modules/gui/plot/img/ScatterView.png +doc/source/modules/gui/plot/img/StackView.png +doc/source/modules/gui/plot/img/StackViewMainWindow.png +doc/source/modules/gui/plot/img/colorScale.png +doc/source/modules/gui/plot/img/colorScaleBar.png +doc/source/modules/gui/plot/img/logColorbar.png +doc/source/modules/gui/plot/img/netArea.png +doc/source/modules/gui/plot/img/netCounts.png +doc/source/modules/gui/plot/img/plot_and_backend.png +doc/source/modules/gui/plot/img/printPreviewMultiPlot.png +doc/source/modules/gui/plot/img/rawArea.png +doc/source/modules/gui/plot/img/rawCounts.png +doc/source/modules/gui/plot/img/roiwidget.png +doc/source/modules/gui/plot/img/statsWidget.png +doc/source/modules/gui/plot/img/tickbar.png +doc/source/modules/gui/plot/stats/index.rst +doc/source/modules/gui/plot/stats/stats.rst +doc/source/modules/gui/plot/stats/statshandler.rst +doc/source/modules/gui/plot/tools/index.rst +doc/source/modules/gui/plot/tools/profile.rst +doc/source/modules/gui/plot/tools/img/CurveLegendsWidget.png +doc/source/modules/gui/plot/tools/img/linearColorbar.png +doc/source/modules/gui/plot3d/actions.rst +doc/source/modules/gui/plot3d/dev.rst +doc/source/modules/gui/plot3d/glutils.rst +doc/source/modules/gui/plot3d/index.rst +doc/source/modules/gui/plot3d/items.rst +doc/source/modules/gui/plot3d/plot3dwidget.rst +doc/source/modules/gui/plot3d/plot3dwindow.rst +doc/source/modules/gui/plot3d/scalarfieldview.rst +doc/source/modules/gui/plot3d/scene.rst +doc/source/modules/gui/plot3d/scenewidget.rst +doc/source/modules/gui/plot3d/scenewindow.rst +doc/source/modules/gui/plot3d/sfviewparamtree.rst +doc/source/modules/gui/plot3d/tools.rst +doc/source/modules/gui/plot3d/utils.rst +doc/source/modules/gui/plot3d/img/GroupPropertiesWidget.png +doc/source/modules/gui/plot3d/img/Plot3DWidget.png +doc/source/modules/gui/plot3d/img/Plot3DWindow.png +doc/source/modules/gui/plot3d/img/PositionInfoWidget.png +doc/source/modules/gui/plot3d/img/SFViewParamTree.png +doc/source/modules/gui/plot3d/img/ScalarFieldView.png +doc/source/modules/gui/plot3d/img/SceneWidget.png +doc/source/modules/gui/plot3d/img/SceneWindow.png +doc/source/modules/gui/widgets/flowlayout.rst +doc/source/modules/gui/widgets/framebrowser.rst +doc/source/modules/gui/widgets/index.rst +doc/source/modules/gui/widgets/periodictable.rst +doc/source/modules/gui/widgets/printpreview.rst +doc/source/modules/gui/widgets/rangeslider.rst +doc/source/modules/gui/widgets/tablewidget.rst +doc/source/modules/gui/widgets/threadpoolpushbutton.rst +doc/source/modules/gui/widgets/waitingpushbutton.rst +doc/source/modules/gui/widgets/img/FrameBrowser.png +doc/source/modules/gui/widgets/img/HorizontalSliderWithBrowser.png +doc/source/modules/gui/widgets/img/PeriodicCombo.png +doc/source/modules/gui/widgets/img/PeriodicList.png +doc/source/modules/gui/widgets/img/PeriodicTable.png +doc/source/modules/gui/widgets/img/RangeSlider.png +doc/source/modules/gui/widgets/img/TableWidget.png +doc/source/modules/gui/widgets/img/ThreadPoolPushButton.png +doc/source/modules/gui/widgets/img/WaitingPushButton.png +doc/source/modules/image/backprojection.rst +doc/source/modules/image/bilinear.rst +doc/source/modules/image/index.rst +doc/source/modules/image/marchingsquares.rst +doc/source/modules/image/medianfilter.rst +doc/source/modules/image/projection.rst +doc/source/modules/image/reconstruction.rst +doc/source/modules/image/shapes.rst +doc/source/modules/image/sift.rst +doc/source/modules/io/commonh5.rst +doc/source/modules/io/configdict.rst +doc/source/modules/io/convert.rst +doc/source/modules/io/dictdump.rst +doc/source/modules/io/fioh5.rst +doc/source/modules/io/h5py_utils.rst +doc/source/modules/io/index.rst +doc/source/modules/io/nxdata.rst +doc/source/modules/io/octaveh5.rst +doc/source/modules/io/specfile.rst +doc/source/modules/io/specfilewrapper.rst +doc/source/modules/io/spech5.rst +doc/source/modules/io/url.rst +doc/source/modules/io/utils.rst +doc/source/modules/math/colormap.rst +doc/source/modules/math/combo.rst +doc/source/modules/math/histogram.rst +doc/source/modules/math/index.rst +doc/source/modules/math/medianfilter.rst +doc/source/modules/math/fit/bgtheories.rst +doc/source/modules/math/fit/filters.rst +doc/source/modules/math/fit/fitmanager.rst +doc/source/modules/math/fit/fittheories.rst +doc/source/modules/math/fit/fittheory.rst +doc/source/modules/math/fit/functions.rst +doc/source/modules/math/fit/index.rst +doc/source/modules/math/fit/leastsq.rst +doc/source/modules/math/fit/peaksearch.rst +doc/source/modules/opencl/codec_cbf.rst +doc/source/modules/opencl/convolution.rst +doc/source/modules/opencl/fbp.rst +doc/source/modules/opencl/index.rst +doc/source/modules/opencl/medfilt.rst +doc/source/modules/opencl/processing.rst +doc/source/modules/opencl/sinofilter.rst +doc/source/modules/opencl/statistics.rst +doc/source/modules/opencl/sift/align.rst +doc/source/modules/opencl/sift/index.rst +doc/source/modules/opencl/sift/match.rst +doc/source/modules/opencl/sift/plan.rst +doc/source/modules/test/index.rst +doc/source/modules/utils/array_like.rst +doc/source/modules/utils/decorators.rst +doc/source/modules/utils/html.rst +doc/source/modules/utils/index.rst +doc/source/modules/utils/retry.rst +doc/source/modules/utils/testutils.rst +doc/source/modules/utils/weakref.rst +doc/source/sample_code/index.rst +doc/source/sample_code/img/animatedicons.png +doc/source/sample_code/img/colormapDialog.png +doc/source/sample_code/img/compareImages.png +doc/source/sample_code/img/compositeline.png +doc/source/sample_code/img/customDataView.png +doc/source/sample_code/img/customHdf5TreeModel.png +doc/source/sample_code/img/customSilxView.png +doc/source/sample_code/img/dropZones.png +doc/source/sample_code/img/exampleBaseline.png +doc/source/sample_code/img/fftPlotAction.png +doc/source/sample_code/img/fileDialog.png +doc/source/sample_code/img/findContours.png +doc/source/sample_code/img/hdf5widget.png +doc/source/sample_code/img/icons.png +doc/source/sample_code/img/imageStack.png +doc/source/sample_code/img/imageview.png +doc/source/sample_code/img/periodicTable.png +doc/source/sample_code/img/plot3dContextMenu.png +doc/source/sample_code/img/plot3dSceneWindow.png +doc/source/sample_code/img/plot3dUpdateScatterFromThread.png +doc/source/sample_code/img/plotClearAction.png +doc/source/sample_code/img/plotContextMenu.png +doc/source/sample_code/img/plotCurveLegendWidget.png +doc/source/sample_code/img/plotInteractiveImageROI.png +doc/source/sample_code/img/plotItemsSelector.png +doc/source/sample_code/img/plotLimits.png +doc/source/sample_code/img/plotProfile.png +doc/source/sample_code/img/plotROIStats.png +doc/source/sample_code/img/plotStats.png +doc/source/sample_code/img/plotUpdateCurveFromThread.png +doc/source/sample_code/img/plotUpdateImageFromGevent.png +doc/source/sample_code/img/plotUpdateImageFromThread.png +doc/source/sample_code/img/plotWidget.png +doc/source/sample_code/img/printPreview.png +doc/source/sample_code/img/scatterMask.png +doc/source/sample_code/img/scatterview.png +doc/source/sample_code/img/shiftPlotAction.png +doc/source/sample_code/img/simplewidget.png +doc/source/sample_code/img/stackView.png +doc/source/sample_code/img/syncPlotLocation.png +doc/source/sample_code/img/syncaxis.png +doc/source/sample_code/img/viewer3DVolume.png +examples/__init__.py +examples/colormapDialog.py +examples/compareImages.py +examples/compositeline.py +examples/customDataView.py +examples/customHdf5TreeModel.py +examples/customSilxView.py +examples/dropZones.py +examples/exampleBaseline.py +examples/fft.png +examples/fftPlotAction.py +examples/fileDialog.py +examples/findContours.py +examples/hdf5widget.py +examples/icons.py +examples/imageStack.py +examples/imageview.py +examples/periodicTable.py +examples/plot3dContextMenu.py +examples/plot3dSceneWindow.py +examples/plot3dUpdateScatterFromThread.py +examples/plotClearAction.py +examples/plotContextMenu.py +examples/plotCurveLegendWidget.py +examples/plotInteractiveImageROI.py +examples/plotItemsSelector.py +examples/plotLimits.py +examples/plotProfile.py +examples/plotROIStats.py +examples/plotStats.py +examples/plotUpdateCurveFromThread.py +examples/plotUpdateImageFromGevent.py +examples/plotUpdateImageFromThread.py +examples/plotWidget.py +examples/printPreview.py +examples/scatterMask.py +examples/scatterview.py +examples/shiftPlotAction.py +examples/simplewidget.py +examples/stackView.py +examples/syncPlotLocation.py +examples/syncaxis.py +examples/viewer3DVolume.py +examples/writetoh5.py +package/debian10/changelog +package/debian10/compat +package/debian10/control +package/debian10/gbp.conf +package/debian10/py3dist-overrides +package/debian10/python-silx-doc.doc-base +package/debian10/rules +package/debian10/watch +package/debian10/patches/0002-use-the-system-mathjax-privacy-breach.patch +package/debian10/patches/0003-do-not-modify-PYTHONPATH-from-setup.py.patch +package/debian10/patches/series +package/debian10/source/format +package/debian10/source/options +package/debian10/tests/control +package/debian11/changelog +package/debian11/control +package/debian11/gbp.conf +package/debian11/py3dist-overrides +package/debian11/python-silx-doc.doc-base +package/debian11/rules +package/debian11/watch +package/debian11/patches/0002-use-the-system-mathjax-privacy-breach.patch +package/debian11/patches/0003-do-not-modify-PYTHONPATH-from-setup.py.patch +package/debian11/patches/series +package/debian11/source/format +package/debian11/source/options +package/debian11/tests/control +package/desktop/org.silx.SilxView.desktop +package/desktop/silx.png +package/desktop/silx.svg +package/desktop/silx.xml +package/windows/README.rst +package/windows/bootstrap-silx-view.py +package/windows/bootstrap.py +package/windows/create-installer.iss.template +package/windows/pyinstaller.spec +package/windows/silx.ico +qtdesigner_plugins/README.rst +qtdesigner_plugins/plot1dplugin.py +qtdesigner_plugins/plot2dplugin.py +qtdesigner_plugins/plotwidgetplugin.py +qtdesigner_plugins/plotwindowplugin.py +src/silx/__init__.py +src/silx/__main__.py +src/silx/_config.py +src/silx/_version.py +src/silx/conftest.py +src/silx.egg-info/PKG-INFO +src/silx.egg-info/SOURCES.txt +src/silx.egg-info/dependency_links.txt +src/silx.egg-info/entry_points.txt +src/silx.egg-info/not-zip-safe +src/silx.egg-info/requires.txt +src/silx.egg-info/top_level.txt +src/silx/app/__init__.py +src/silx/app/convert.py +src/silx/app/test_.py +src/silx/app/test/__init__.py +src/silx/app/test/test_convert.py +src/silx/app/view/About.py +src/silx/app/view/ApplicationContext.py +src/silx/app/view/CustomNxdataWidget.py +src/silx/app/view/DataPanel.py +src/silx/app/view/Viewer.py +src/silx/app/view/__init__.py +src/silx/app/view/main.py +src/silx/app/view/utils.py +src/silx/app/view/test/__init__.py +src/silx/app/view/test/test_launcher.py +src/silx/app/view/test/test_view.py +src/silx/gui/__init__.py +src/silx/gui/colors.py +src/silx/gui/conftest.py +src/silx/gui/console.py +src/silx/gui/icons.py +src/silx/gui/printer.py +src/silx/gui/_glutils/Context.py +src/silx/gui/_glutils/FramebufferTexture.py +src/silx/gui/_glutils/OpenGLWidget.py +src/silx/gui/_glutils/Program.py +src/silx/gui/_glutils/Texture.py +src/silx/gui/_glutils/VertexBuffer.py +src/silx/gui/_glutils/__init__.py +src/silx/gui/_glutils/font.py +src/silx/gui/_glutils/gl.py +src/silx/gui/_glutils/utils.py +src/silx/gui/data/ArrayTableModel.py +src/silx/gui/data/ArrayTableWidget.py +src/silx/gui/data/DataViewer.py +src/silx/gui/data/DataViewerFrame.py +src/silx/gui/data/DataViewerSelector.py +src/silx/gui/data/DataViews.py +src/silx/gui/data/Hdf5TableView.py +src/silx/gui/data/HexaTableView.py +src/silx/gui/data/NXdataWidgets.py +src/silx/gui/data/NumpyAxesSelector.py +src/silx/gui/data/RecordTableView.py +src/silx/gui/data/TextFormatter.py +src/silx/gui/data/_RecordPlot.py +src/silx/gui/data/_VolumeWindow.py +src/silx/gui/data/__init__.py +src/silx/gui/data/test/__init__.py +src/silx/gui/data/test/test_arraywidget.py +src/silx/gui/data/test/test_dataviewer.py +src/silx/gui/data/test/test_numpyaxesselector.py +src/silx/gui/data/test/test_textformatter.py +src/silx/gui/dialog/AbstractDataFileDialog.py +src/silx/gui/dialog/ColormapDialog.py +src/silx/gui/dialog/DataFileDialog.py +src/silx/gui/dialog/DatasetDialog.py +src/silx/gui/dialog/FileTypeComboBox.py +src/silx/gui/dialog/GroupDialog.py +src/silx/gui/dialog/ImageFileDialog.py +src/silx/gui/dialog/SafeFileIconProvider.py +src/silx/gui/dialog/SafeFileSystemModel.py +src/silx/gui/dialog/__init__.py +src/silx/gui/dialog/utils.py +src/silx/gui/dialog/test/__init__.py +src/silx/gui/dialog/test/test_colormapdialog.py +src/silx/gui/dialog/test/test_datafiledialog.py +src/silx/gui/dialog/test/test_imagefiledialog.py +src/silx/gui/fit/BackgroundWidget.py +src/silx/gui/fit/FitConfig.py +src/silx/gui/fit/FitWidget.py +src/silx/gui/fit/FitWidgets.py +src/silx/gui/fit/Parameters.py +src/silx/gui/fit/__init__.py +src/silx/gui/fit/test/__init__.py +src/silx/gui/fit/test/testBackgroundWidget.py +src/silx/gui/fit/test/testFitConfig.py +src/silx/gui/fit/test/testFitWidget.py +src/silx/gui/hdf5/Hdf5Formatter.py +src/silx/gui/hdf5/Hdf5HeaderView.py +src/silx/gui/hdf5/Hdf5Item.py +src/silx/gui/hdf5/Hdf5LoadingItem.py +src/silx/gui/hdf5/Hdf5Node.py +src/silx/gui/hdf5/Hdf5TreeModel.py +src/silx/gui/hdf5/Hdf5TreeView.py +src/silx/gui/hdf5/NexusSortFilterProxyModel.py +src/silx/gui/hdf5/__init__.py +src/silx/gui/hdf5/_utils.py +src/silx/gui/hdf5/test/__init__.py +src/silx/gui/hdf5/test/test_hdf5.py +src/silx/gui/plot/AlphaSlider.py +src/silx/gui/plot/ColorBar.py +src/silx/gui/plot/Colormap.py +src/silx/gui/plot/ColormapDialog.py +src/silx/gui/plot/Colors.py +src/silx/gui/plot/CompareImages.py +src/silx/gui/plot/ComplexImageView.py +src/silx/gui/plot/CurvesROIWidget.py +src/silx/gui/plot/ImageStack.py +src/silx/gui/plot/ImageView.py +src/silx/gui/plot/Interaction.py +src/silx/gui/plot/ItemsSelectionDialog.py +src/silx/gui/plot/LegendSelector.py +src/silx/gui/plot/LimitsHistory.py +src/silx/gui/plot/MaskToolsWidget.py +src/silx/gui/plot/PlotActions.py +src/silx/gui/plot/PlotEvents.py +src/silx/gui/plot/PlotInteraction.py +src/silx/gui/plot/PlotToolButtons.py +src/silx/gui/plot/PlotTools.py +src/silx/gui/plot/PlotWidget.py +src/silx/gui/plot/PlotWindow.py +src/silx/gui/plot/PrintPreviewToolButton.py +src/silx/gui/plot/Profile.py +src/silx/gui/plot/ProfileMainWindow.py +src/silx/gui/plot/ROIStatsWidget.py +src/silx/gui/plot/ScatterMaskToolsWidget.py +src/silx/gui/plot/ScatterView.py +src/silx/gui/plot/StackView.py +src/silx/gui/plot/StatsWidget.py +src/silx/gui/plot/_BaseMaskToolsWidget.py +src/silx/gui/plot/__init__.py +src/silx/gui/plot/_utils/__init__.py +src/silx/gui/plot/_utils/delaunay.py +src/silx/gui/plot/_utils/dtime_ticklayout.py +src/silx/gui/plot/_utils/panzoom.py +src/silx/gui/plot/_utils/ticklayout.py +src/silx/gui/plot/_utils/test/__init__.py +src/silx/gui/plot/_utils/test/test_dtime_ticklayout.py +src/silx/gui/plot/_utils/test/test_ticklayout.py +src/silx/gui/plot/actions/PlotAction.py +src/silx/gui/plot/actions/PlotToolAction.py +src/silx/gui/plot/actions/__init__.py +src/silx/gui/plot/actions/control.py +src/silx/gui/plot/actions/fit.py +src/silx/gui/plot/actions/histogram.py +src/silx/gui/plot/actions/io.py +src/silx/gui/plot/actions/medfilt.py +src/silx/gui/plot/actions/mode.py +src/silx/gui/plot/backends/BackendBase.py +src/silx/gui/plot/backends/BackendMatplotlib.py +src/silx/gui/plot/backends/BackendOpenGL.py +src/silx/gui/plot/backends/__init__.py +src/silx/gui/plot/backends/glutils/GLPlotCurve.py +src/silx/gui/plot/backends/glutils/GLPlotFrame.py +src/silx/gui/plot/backends/glutils/GLPlotImage.py +src/silx/gui/plot/backends/glutils/GLPlotItem.py +src/silx/gui/plot/backends/glutils/GLPlotTriangles.py +src/silx/gui/plot/backends/glutils/GLSupport.py +src/silx/gui/plot/backends/glutils/GLText.py +src/silx/gui/plot/backends/glutils/GLTexture.py +src/silx/gui/plot/backends/glutils/PlotImageFile.py +src/silx/gui/plot/backends/glutils/__init__.py +src/silx/gui/plot/items/__init__.py +src/silx/gui/plot/items/_arc_roi.py +src/silx/gui/plot/items/_band_roi.py +src/silx/gui/plot/items/_pick.py +src/silx/gui/plot/items/_roi_base.py +src/silx/gui/plot/items/axis.py +src/silx/gui/plot/items/complex.py +src/silx/gui/plot/items/core.py +src/silx/gui/plot/items/curve.py +src/silx/gui/plot/items/histogram.py +src/silx/gui/plot/items/image.py +src/silx/gui/plot/items/image_aggregated.py +src/silx/gui/plot/items/marker.py +src/silx/gui/plot/items/roi.py +src/silx/gui/plot/items/scatter.py +src/silx/gui/plot/items/shape.py +src/silx/gui/plot/matplotlib/Colormap.py +src/silx/gui/plot/matplotlib/__init__.py +src/silx/gui/plot/stats/__init__.py +src/silx/gui/plot/stats/stats.py +src/silx/gui/plot/stats/statshandler.py +src/silx/gui/plot/test/__init__.py +src/silx/gui/plot/test/testAlphaSlider.py +src/silx/gui/plot/test/testColorBar.py +src/silx/gui/plot/test/testCompareImages.py +src/silx/gui/plot/test/testComplexImageView.py +src/silx/gui/plot/test/testCurvesROIWidget.py +src/silx/gui/plot/test/testImageStack.py +src/silx/gui/plot/test/testImageView.py +src/silx/gui/plot/test/testInteraction.py +src/silx/gui/plot/test/testItem.py +src/silx/gui/plot/test/testLegendSelector.py +src/silx/gui/plot/test/testLimitConstraints.py +src/silx/gui/plot/test/testMaskToolsWidget.py +src/silx/gui/plot/test/testPixelIntensityHistoAction.py +src/silx/gui/plot/test/testPlotActions.py +src/silx/gui/plot/test/testPlotInteraction.py +src/silx/gui/plot/test/testPlotWidget.py +src/silx/gui/plot/test/testPlotWidgetNoBackend.py +src/silx/gui/plot/test/testPlotWindow.py +src/silx/gui/plot/test/testRoiStatsWidget.py +src/silx/gui/plot/test/testSaveAction.py +src/silx/gui/plot/test/testScatterMaskToolsWidget.py +src/silx/gui/plot/test/testScatterView.py +src/silx/gui/plot/test/testStackView.py +src/silx/gui/plot/test/testStats.py +src/silx/gui/plot/test/testUtilsAxis.py +src/silx/gui/plot/test/utils.py +src/silx/gui/plot/tools/CurveLegendsWidget.py +src/silx/gui/plot/tools/LimitsToolBar.py +src/silx/gui/plot/tools/PositionInfo.py +src/silx/gui/plot/tools/RadarView.py +src/silx/gui/plot/tools/__init__.py +src/silx/gui/plot/tools/roi.py +src/silx/gui/plot/tools/toolbars.py +src/silx/gui/plot/tools/profile/ScatterProfileToolBar.py +src/silx/gui/plot/tools/profile/__init__.py +src/silx/gui/plot/tools/profile/core.py +src/silx/gui/plot/tools/profile/editors.py +src/silx/gui/plot/tools/profile/manager.py +src/silx/gui/plot/tools/profile/rois.py +src/silx/gui/plot/tools/profile/toolbar.py +src/silx/gui/plot/tools/test/__init__.py +src/silx/gui/plot/tools/test/testCurveLegendsWidget.py +src/silx/gui/plot/tools/test/testProfile.py +src/silx/gui/plot/tools/test/testROI.py +src/silx/gui/plot/tools/test/testScatterProfileToolBar.py +src/silx/gui/plot/tools/test/testTools.py +src/silx/gui/plot/utils/__init__.py +src/silx/gui/plot/utils/axis.py +src/silx/gui/plot/utils/intersections.py +src/silx/gui/plot3d/ParamTreeView.py +src/silx/gui/plot3d/Plot3DWidget.py +src/silx/gui/plot3d/Plot3DWindow.py +src/silx/gui/plot3d/SFViewParamTree.py +src/silx/gui/plot3d/ScalarFieldView.py +src/silx/gui/plot3d/SceneWidget.py +src/silx/gui/plot3d/SceneWindow.py +src/silx/gui/plot3d/__init__.py +src/silx/gui/plot3d/conftest.py +src/silx/gui/plot3d/_model/__init__.py +src/silx/gui/plot3d/_model/core.py +src/silx/gui/plot3d/_model/items.py +src/silx/gui/plot3d/_model/model.py +src/silx/gui/plot3d/actions/Plot3DAction.py +src/silx/gui/plot3d/actions/__init__.py +src/silx/gui/plot3d/actions/io.py +src/silx/gui/plot3d/actions/mode.py +src/silx/gui/plot3d/actions/viewpoint.py +src/silx/gui/plot3d/items/__init__.py +src/silx/gui/plot3d/items/_pick.py +src/silx/gui/plot3d/items/clipplane.py +src/silx/gui/plot3d/items/core.py +src/silx/gui/plot3d/items/image.py +src/silx/gui/plot3d/items/mesh.py +src/silx/gui/plot3d/items/mixins.py +src/silx/gui/plot3d/items/scatter.py +src/silx/gui/plot3d/items/volume.py +src/silx/gui/plot3d/scene/__init__.py +src/silx/gui/plot3d/scene/axes.py +src/silx/gui/plot3d/scene/camera.py +src/silx/gui/plot3d/scene/core.py +src/silx/gui/plot3d/scene/cutplane.py +src/silx/gui/plot3d/scene/event.py +src/silx/gui/plot3d/scene/function.py +src/silx/gui/plot3d/scene/interaction.py +src/silx/gui/plot3d/scene/primitives.py +src/silx/gui/plot3d/scene/text.py +src/silx/gui/plot3d/scene/transform.py +src/silx/gui/plot3d/scene/utils.py +src/silx/gui/plot3d/scene/viewport.py +src/silx/gui/plot3d/scene/window.py +src/silx/gui/plot3d/scene/test/__init__.py +src/silx/gui/plot3d/scene/test/test_transform.py +src/silx/gui/plot3d/scene/test/test_utils.py +src/silx/gui/plot3d/test/__init__.py +src/silx/gui/plot3d/test/testGL.py +src/silx/gui/plot3d/test/testScalarFieldView.py +src/silx/gui/plot3d/test/testSceneWidget.py +src/silx/gui/plot3d/test/testSceneWidgetPicking.py +src/silx/gui/plot3d/test/testSceneWindow.py +src/silx/gui/plot3d/test/testStatsWidget.py +src/silx/gui/plot3d/tools/GroupPropertiesWidget.py +src/silx/gui/plot3d/tools/PositionInfoWidget.py +src/silx/gui/plot3d/tools/ViewpointTools.py +src/silx/gui/plot3d/tools/__init__.py +src/silx/gui/plot3d/tools/toolbars.py +src/silx/gui/plot3d/tools/test/__init__.py +src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py +src/silx/gui/plot3d/utils/__init__.py +src/silx/gui/plot3d/utils/mng.py +src/silx/gui/qt/__init__.py +src/silx/gui/qt/_pyqt6.py +src/silx/gui/qt/_pyside_dynamic.py +src/silx/gui/qt/_qt.py +src/silx/gui/qt/_utils.py +src/silx/gui/qt/inspect.py +src/silx/gui/test/__init__.py +src/silx/gui/test/test_colors.py +src/silx/gui/test/test_console.py +src/silx/gui/test/test_icons.py +src/silx/gui/test/test_qt.py +src/silx/gui/test/utils.py +src/silx/gui/utils/__init__.py +src/silx/gui/utils/concurrent.py +src/silx/gui/utils/image.py +src/silx/gui/utils/matplotlib.py +src/silx/gui/utils/projecturl.py +src/silx/gui/utils/qtutils.py +src/silx/gui/utils/signal.py +src/silx/gui/utils/testutils.py +src/silx/gui/utils/glutils/__init__.py +src/silx/gui/utils/test/__init__.py +src/silx/gui/utils/test/test.py +src/silx/gui/utils/test/test_async.py +src/silx/gui/utils/test/test_glutils.py +src/silx/gui/utils/test/test_image.py +src/silx/gui/utils/test/test_qtutils.py +src/silx/gui/utils/test/test_testutils.py +src/silx/gui/widgets/BoxLayoutDockWidget.py +src/silx/gui/widgets/ColormapNameComboBox.py +src/silx/gui/widgets/ElidedLabel.py +src/silx/gui/widgets/FloatEdit.py +src/silx/gui/widgets/FlowLayout.py +src/silx/gui/widgets/FormGridLayout.py +src/silx/gui/widgets/FrameBrowser.py +src/silx/gui/widgets/HierarchicalTableView.py +src/silx/gui/widgets/LegendIconWidget.py +src/silx/gui/widgets/MedianFilterDialog.py +src/silx/gui/widgets/MultiModeAction.py +src/silx/gui/widgets/PeriodicTable.py +src/silx/gui/widgets/PrintGeometryDialog.py +src/silx/gui/widgets/PrintPreview.py +src/silx/gui/widgets/RangeSlider.py +src/silx/gui/widgets/TableWidget.py +src/silx/gui/widgets/ThreadPoolPushButton.py +src/silx/gui/widgets/UrlSelectionTable.py +src/silx/gui/widgets/WaitingPushButton.py +src/silx/gui/widgets/__init__.py +src/silx/gui/widgets/test/__init__.py +src/silx/gui/widgets/test/test_boxlayoutdockwidget.py +src/silx/gui/widgets/test/test_elidedlabel.py +src/silx/gui/widgets/test/test_flowlayout.py +src/silx/gui/widgets/test/test_framebrowser.py +src/silx/gui/widgets/test/test_hierarchicaltableview.py +src/silx/gui/widgets/test/test_legendiconwidget.py +src/silx/gui/widgets/test/test_periodictable.py +src/silx/gui/widgets/test/test_printpreview.py +src/silx/gui/widgets/test/test_rangeslider.py +src/silx/gui/widgets/test/test_tablewidget.py +src/silx/gui/widgets/test/test_threadpoolpushbutton.py +src/silx/image/__init__.py +src/silx/image/_boundingbox.py +src/silx/image/backprojection.py +src/silx/image/bilinear.pyx +src/silx/image/medianfilter.py +src/silx/image/phantomgenerator.py +src/silx/image/projection.py +src/silx/image/reconstruction.py +src/silx/image/shapes.pyx +src/silx/image/sift.py +src/silx/image/tomography.py +src/silx/image/utils.py +src/silx/image/marchingsquares/__init__.py +src/silx/image/marchingsquares/_mergeimpl.pyx +src/silx/image/marchingsquares/_skimage.py +src/silx/image/marchingsquares/include/patterns.h +src/silx/image/marchingsquares/test/__init__.py +src/silx/image/marchingsquares/test/test_funcapi.py +src/silx/image/marchingsquares/test/test_mergeimpl.py +src/silx/image/test/__init__.py +src/silx/image/test/test_bb.py +src/silx/image/test/test_bilinear.py +src/silx/image/test/test_medianfilter.py +src/silx/image/test/test_shapes.py +src/silx/image/test/test_tomography.py +src/silx/io/__init__.py +src/silx/io/commonh5.py +src/silx/io/configdict.py +src/silx/io/convert.py +src/silx/io/dictdump.py +src/silx/io/fabioh5.py +src/silx/io/fioh5.py +src/silx/io/h5py_utils.py +src/silx/io/octaveh5.py +src/silx/io/rawh5.py +src/silx/io/specfile.pyx +src/silx/io/specfile_wrapper.pxd +src/silx/io/specfilewrapper.py +src/silx/io/spech5.py +src/silx/io/spectoh5.py +src/silx/io/url.py +src/silx/io/utils.py +src/silx/io/nxdata/__init__.py +src/silx/io/nxdata/_utils.py +src/silx/io/nxdata/parse.py +src/silx/io/nxdata/write.py +src/silx/io/specfile/include/Lists.h +src/silx/io/specfile/include/SpecFile.h +src/silx/io/specfile/include/SpecFileCython.h +src/silx/io/specfile/include/SpecFileP.h +src/silx/io/specfile/include/locale_management.h +src/silx/io/specfile/src/locale_management.c +src/silx/io/specfile/src/sfdata.c +src/silx/io/specfile/src/sfheader.c +src/silx/io/specfile/src/sfindex.c +src/silx/io/specfile/src/sfinit.c +src/silx/io/specfile/src/sflabel.c +src/silx/io/specfile/src/sflists.c +src/silx/io/specfile/src/sfmca.c +src/silx/io/specfile/src/sftools.c +src/silx/io/specfile/src/sfwrite.c +src/silx/io/test/__init__.py +src/silx/io/test/test_commonh5.py +src/silx/io/test/test_dictdump.py +src/silx/io/test/test_fabioh5.py +src/silx/io/test/test_fioh5.py +src/silx/io/test/test_h5py_utils.py +src/silx/io/test/test_nxdata.py +src/silx/io/test/test_octaveh5.py +src/silx/io/test/test_rawh5.py +src/silx/io/test/test_specfile.py +src/silx/io/test/test_specfilewrapper.py +src/silx/io/test/test_spech5.py +src/silx/io/test/test_spectoh5.py +src/silx/io/test/test_url.py +src/silx/io/test/test_utils.py +src/silx/io/test/test_write_to_h5.py +src/silx/math/__init__.py +src/silx/math/_colormap.pyx +src/silx/math/calibration.py +src/silx/math/chistogramnd.pyx +src/silx/math/chistogramnd_lut.pyx +src/silx/math/colormap.py +src/silx/math/combo.pyx +src/silx/math/histogram.py +src/silx/math/histogramnd_c.pxd +src/silx/math/interpolate.pyx +src/silx/math/marchingcubes.pyx +src/silx/math/math_compatibility.pxd +src/silx/math/mc.pxd +src/silx/math/fft/__init__.py +src/silx/math/fft/basefft.py +src/silx/math/fft/clfft.py +src/silx/math/fft/cufft.py +src/silx/math/fft/fft.py +src/silx/math/fft/fftw.py +src/silx/math/fft/npfft.py +src/silx/math/fft/test/__init__.py +src/silx/math/fft/test/test_fft.py +src/silx/math/fit/__init__.py +src/silx/math/fit/bgtheories.py +src/silx/math/fit/filters.pyx +src/silx/math/fit/filters_wrapper.pxd +src/silx/math/fit/fitmanager.py +src/silx/math/fit/fittheories.py +src/silx/math/fit/fittheory.py +src/silx/math/fit/functions.pyx +src/silx/math/fit/functions_wrapper.pxd +src/silx/math/fit/leastsq.py +src/silx/math/fit/peaks.pyx +src/silx/math/fit/peaks_wrapper.pxd +src/silx/math/fit/filters/include/filters.h +src/silx/math/fit/filters/src/smoothnd.c +src/silx/math/fit/filters/src/snip1d.c +src/silx/math/fit/filters/src/snip2d.c +src/silx/math/fit/filters/src/snip3d.c +src/silx/math/fit/filters/src/strip.c +src/silx/math/fit/functions/include/functions.h +src/silx/math/fit/functions/src/funs.c +src/silx/math/fit/peaks/include/peaks.h +src/silx/math/fit/peaks/src/peaks.c +src/silx/math/fit/test/__init__.py +src/silx/math/fit/test/test_bgtheories.py +src/silx/math/fit/test/test_filters.py +src/silx/math/fit/test/test_fit.py +src/silx/math/fit/test/test_fitmanager.py +src/silx/math/fit/test/test_functions.py +src/silx/math/fit/test/test_peaks.py +src/silx/math/histogramnd/include/histogramnd_c.h +src/silx/math/histogramnd/include/templates.h +src/silx/math/histogramnd/include/msvc/stdint.h +src/silx/math/histogramnd/src/histogramnd_c.c +src/silx/math/histogramnd/src/histogramnd_template.c +src/silx/math/include/math_compatibility.h +src/silx/math/marchingcubes/mc.hpp +src/silx/math/marchingcubes/mc_lut.cpp +src/silx/math/medianfilter/__init__.py +src/silx/math/medianfilter/median_filter.pxd +src/silx/math/medianfilter/medianfilter.pyx +src/silx/math/medianfilter/include/median_filter.hpp +src/silx/math/medianfilter/test/__init__.py +src/silx/math/medianfilter/test/benchmark.py +src/silx/math/medianfilter/test/test_medianfilter.py +src/silx/math/test/__init__.py +src/silx/math/test/benchmark_combo.py +src/silx/math/test/histo_benchmarks.py +src/silx/math/test/test_HistogramndLut_nominal.py +src/silx/math/test/test_calibration.py +src/silx/math/test/test_colormap.py +src/silx/math/test/test_combo.py +src/silx/math/test/test_histogramnd_error.py +src/silx/math/test/test_histogramnd_nominal.py +src/silx/math/test/test_histogramnd_vs_np.py +src/silx/math/test/test_interpolate.py +src/silx/math/test/test_marchingcubes.py +src/silx/opencl/__init__.py +src/silx/opencl/backprojection.py +src/silx/opencl/common.py +src/silx/opencl/conftest.py +src/silx/opencl/convolution.py +src/silx/opencl/image.py +src/silx/opencl/linalg.py +src/silx/opencl/medfilt.py +src/silx/opencl/processing.py +src/silx/opencl/projection.py +src/silx/opencl/reconstruction.py +src/silx/opencl/sinofilter.py +src/silx/opencl/sparse.py +src/silx/opencl/statistics.py +src/silx/opencl/utils.py +src/silx/opencl/codec/__init__.py +src/silx/opencl/codec/byte_offset.py +src/silx/opencl/codec/test/__init__.py +src/silx/opencl/codec/test/test_byte_offset.py +src/silx/opencl/sift/__init__.py +src/silx/opencl/sift/alignment.py +src/silx/opencl/sift/match.py +src/silx/opencl/sift/param.py +src/silx/opencl/sift/plan.py +src/silx/opencl/sift/sift.py +src/silx/opencl/sift/utils.py +src/silx/opencl/sift/test/__init__.py +src/silx/opencl/sift/test/test_algebra.py +src/silx/opencl/sift/test/test_align.py +src/silx/opencl/sift/test/test_convol.py +src/silx/opencl/sift/test/test_gaussian.py +src/silx/opencl/sift/test/test_image.py +src/silx/opencl/sift/test/test_image_functions.py +src/silx/opencl/sift/test/test_image_setup.py +src/silx/opencl/sift/test/test_keypoints.py +src/silx/opencl/sift/test/test_matching.py +src/silx/opencl/sift/test/test_preproc.py +src/silx/opencl/sift/test/test_reductions.py +src/silx/opencl/sift/test/test_transform.py +src/silx/opencl/test/__init__.py +src/silx/opencl/test/test_addition.py +src/silx/opencl/test/test_array_utils.py +src/silx/opencl/test/test_backprojection.py +src/silx/opencl/test/test_convolution.py +src/silx/opencl/test/test_doubleword.py +src/silx/opencl/test/test_image.py +src/silx/opencl/test/test_kahan.py +src/silx/opencl/test/test_linalg.py +src/silx/opencl/test/test_medfilt.py +src/silx/opencl/test/test_projection.py +src/silx/opencl/test/test_sparse.py +src/silx/opencl/test/test_stats.py +src/silx/resources/__init__.py +src/silx/resources/gui/colormaps/cividis.npy +src/silx/resources/gui/colormaps/inferno.npy +src/silx/resources/gui/colormaps/magma.npy +src/silx/resources/gui/colormaps/plasma.npy +src/silx/resources/gui/colormaps/viridis.npy +src/silx/resources/gui/icons/3d-plane-normal-x.png +src/silx/resources/gui/icons/3d-plane-normal-x.svg +src/silx/resources/gui/icons/3d-plane-normal-y.png +src/silx/resources/gui/icons/3d-plane-normal-y.svg +src/silx/resources/gui/icons/3d-plane-normal-z.png +src/silx/resources/gui/icons/3d-plane-normal-z.svg +src/silx/resources/gui/icons/3d-plane-pan.png +src/silx/resources/gui/icons/3d-plane-pan.svg +src/silx/resources/gui/icons/3d-plane.png +src/silx/resources/gui/icons/3d-plane.svg +src/silx/resources/gui/icons/add-range-horizontal.png +src/silx/resources/gui/icons/add-range-horizontal.svg +src/silx/resources/gui/icons/add-shape-arc.png +src/silx/resources/gui/icons/add-shape-arc.svg +src/silx/resources/gui/icons/add-shape-circle.png +src/silx/resources/gui/icons/add-shape-circle.svg +src/silx/resources/gui/icons/add-shape-cross.png +src/silx/resources/gui/icons/add-shape-cross.svg +src/silx/resources/gui/icons/add-shape-diagonal.png +src/silx/resources/gui/icons/add-shape-diagonal.svg +src/silx/resources/gui/icons/add-shape-ellipse.png +src/silx/resources/gui/icons/add-shape-ellipse.svg +src/silx/resources/gui/icons/add-shape-horizontal.png +src/silx/resources/gui/icons/add-shape-horizontal.svg +src/silx/resources/gui/icons/add-shape-point.png +src/silx/resources/gui/icons/add-shape-point.svg +src/silx/resources/gui/icons/add-shape-polygon.png +src/silx/resources/gui/icons/add-shape-polygon.svg +src/silx/resources/gui/icons/add-shape-rectangle.png +src/silx/resources/gui/icons/add-shape-rectangle.svg +src/silx/resources/gui/icons/add-shape-rotated-rectangle.png +src/silx/resources/gui/icons/add-shape-rotated-rectangle.svg +src/silx/resources/gui/icons/add-shape-unknown.png +src/silx/resources/gui/icons/add-shape-unknown.svg +src/silx/resources/gui/icons/add-shape-vertical.png +src/silx/resources/gui/icons/add-shape-vertical.svg +src/silx/resources/gui/icons/add.png +src/silx/resources/gui/icons/add.svg +src/silx/resources/gui/icons/aggregation-mode.png +src/silx/resources/gui/icons/aggregation-mode.svg +src/silx/resources/gui/icons/arrow-keys.png +src/silx/resources/gui/icons/arrow-keys.svg +src/silx/resources/gui/icons/axis.png +src/silx/resources/gui/icons/axis.svg +src/silx/resources/gui/icons/backend-opengl.png +src/silx/resources/gui/icons/backend-opengl.svg +src/silx/resources/gui/icons/camera.png +src/silx/resources/gui/icons/camera.svg +src/silx/resources/gui/icons/clipboard.png +src/silx/resources/gui/icons/clipboard.svg +src/silx/resources/gui/icons/close.png +src/silx/resources/gui/icons/close.svg +src/silx/resources/gui/icons/colorbar.png +src/silx/resources/gui/icons/colorbar.svg +src/silx/resources/gui/icons/colormap-histogram.png +src/silx/resources/gui/icons/colormap-histogram.svg +src/silx/resources/gui/icons/colormap-none.png +src/silx/resources/gui/icons/colormap-none.svg +src/silx/resources/gui/icons/colormap-norm-arcsinh.png +src/silx/resources/gui/icons/colormap-norm-arcsinh.svg +src/silx/resources/gui/icons/colormap-norm-gamma.png +src/silx/resources/gui/icons/colormap-norm-gamma.svg +src/silx/resources/gui/icons/colormap-norm-linear.png +src/silx/resources/gui/icons/colormap-norm-linear.svg +src/silx/resources/gui/icons/colormap-norm-log.png +src/silx/resources/gui/icons/colormap-norm-log.svg +src/silx/resources/gui/icons/colormap-norm-sqrt.png +src/silx/resources/gui/icons/colormap-norm-sqrt.svg +src/silx/resources/gui/icons/colormap-range.png +src/silx/resources/gui/icons/colormap-range.svg +src/silx/resources/gui/icons/colormap.png +src/silx/resources/gui/icons/colormap.svg +src/silx/resources/gui/icons/compare-align-auto.png +src/silx/resources/gui/icons/compare-align-auto.svg +src/silx/resources/gui/icons/compare-align-center.png +src/silx/resources/gui/icons/compare-align-center.svg +src/silx/resources/gui/icons/compare-align-origin.png +src/silx/resources/gui/icons/compare-align-origin.svg +src/silx/resources/gui/icons/compare-align-stretch.png +src/silx/resources/gui/icons/compare-align-stretch.svg +src/silx/resources/gui/icons/compare-keypoints.png +src/silx/resources/gui/icons/compare-keypoints.svg +src/silx/resources/gui/icons/compare-mode-a-minus-b.png +src/silx/resources/gui/icons/compare-mode-a-minus-b.svg +src/silx/resources/gui/icons/compare-mode-a.png +src/silx/resources/gui/icons/compare-mode-a.svg +src/silx/resources/gui/icons/compare-mode-b.png +src/silx/resources/gui/icons/compare-mode-b.svg +src/silx/resources/gui/icons/compare-mode-hline.png +src/silx/resources/gui/icons/compare-mode-hline.svg +src/silx/resources/gui/icons/compare-mode-rb-channel.png +src/silx/resources/gui/icons/compare-mode-rb-channel.svg +src/silx/resources/gui/icons/compare-mode-rbneg-channel.png +src/silx/resources/gui/icons/compare-mode-rbneg-channel.svg +src/silx/resources/gui/icons/compare-mode-vline.png +src/silx/resources/gui/icons/compare-mode-vline.svg +src/silx/resources/gui/icons/crop.png +src/silx/resources/gui/icons/crop.svg +src/silx/resources/gui/icons/crosshair.png +src/silx/resources/gui/icons/crosshair.svg +src/silx/resources/gui/icons/cube-back.png +src/silx/resources/gui/icons/cube-back.svg +src/silx/resources/gui/icons/cube-bottom.png +src/silx/resources/gui/icons/cube-bottom.svg +src/silx/resources/gui/icons/cube-front.png +src/silx/resources/gui/icons/cube-front.svg +src/silx/resources/gui/icons/cube-left.png +src/silx/resources/gui/icons/cube-left.svg +src/silx/resources/gui/icons/cube-right.png +src/silx/resources/gui/icons/cube-right.svg +src/silx/resources/gui/icons/cube-rotate.png +src/silx/resources/gui/icons/cube-rotate.svg +src/silx/resources/gui/icons/cube-top.png +src/silx/resources/gui/icons/cube-top.svg +src/silx/resources/gui/icons/cube.png +src/silx/resources/gui/icons/cube.svg +src/silx/resources/gui/icons/description-description.png +src/silx/resources/gui/icons/description-description.svg +src/silx/resources/gui/icons/description-error.png +src/silx/resources/gui/icons/description-error.svg +src/silx/resources/gui/icons/description-name.png +src/silx/resources/gui/icons/description-name.svg +src/silx/resources/gui/icons/description-program.png +src/silx/resources/gui/icons/description-program.svg +src/silx/resources/gui/icons/description-title.png +src/silx/resources/gui/icons/description-title.svg +src/silx/resources/gui/icons/description-value.png +src/silx/resources/gui/icons/description-value.svg +src/silx/resources/gui/icons/document-open.png +src/silx/resources/gui/icons/document-open.svg +src/silx/resources/gui/icons/document-print.png +src/silx/resources/gui/icons/document-print.svg +src/silx/resources/gui/icons/document-save.png +src/silx/resources/gui/icons/document-save.svg +src/silx/resources/gui/icons/draw-brush.png +src/silx/resources/gui/icons/draw-brush.svg +src/silx/resources/gui/icons/draw-pencil.png +src/silx/resources/gui/icons/draw-pencil.svg +src/silx/resources/gui/icons/draw-rubber.png +src/silx/resources/gui/icons/draw-rubber.svg +src/silx/resources/gui/icons/edit-copy.png +src/silx/resources/gui/icons/edit-copy.svg +src/silx/resources/gui/icons/eye.png +src/silx/resources/gui/icons/eye.svg +src/silx/resources/gui/icons/first.png +src/silx/resources/gui/icons/first.svg +src/silx/resources/gui/icons/folder.png +src/silx/resources/gui/icons/folder.svg +src/silx/resources/gui/icons/image-mask.png +src/silx/resources/gui/icons/image-mask.svg +src/silx/resources/gui/icons/image-select-add.png +src/silx/resources/gui/icons/image-select-add.svg +src/silx/resources/gui/icons/image-select-box.png +src/silx/resources/gui/icons/image-select-box.svg +src/silx/resources/gui/icons/image-select-brush.png +src/silx/resources/gui/icons/image-select-brush.svg +src/silx/resources/gui/icons/image-select-erase-rubber.png +src/silx/resources/gui/icons/image-select-erase-rubber.svg +src/silx/resources/gui/icons/image-select-erase.png +src/silx/resources/gui/icons/image-select-erase.svg +src/silx/resources/gui/icons/image.png +src/silx/resources/gui/icons/image.svg +src/silx/resources/gui/icons/item-0dim.png +src/silx/resources/gui/icons/item-0dim.svg +src/silx/resources/gui/icons/item-1dim.png +src/silx/resources/gui/icons/item-1dim.svg +src/silx/resources/gui/icons/item-2dim.png +src/silx/resources/gui/icons/item-2dim.svg +src/silx/resources/gui/icons/item-3dim.png +src/silx/resources/gui/icons/item-3dim.svg +src/silx/resources/gui/icons/item-ndim.png +src/silx/resources/gui/icons/item-ndim.svg +src/silx/resources/gui/icons/item-none.png +src/silx/resources/gui/icons/item-none.svg +src/silx/resources/gui/icons/item-object.png +src/silx/resources/gui/icons/item-object.svg +src/silx/resources/gui/icons/last.png +src/silx/resources/gui/icons/last.svg +src/silx/resources/gui/icons/layer-nx.png +src/silx/resources/gui/icons/layer-nx.svg +src/silx/resources/gui/icons/mask-clear-all.png +src/silx/resources/gui/icons/mask-clear-all.svg +src/silx/resources/gui/icons/mask-clear.png +src/silx/resources/gui/icons/mask-clear.svg +src/silx/resources/gui/icons/mask-invert.png +src/silx/resources/gui/icons/mask-invert.svg +src/silx/resources/gui/icons/math-amplitude.png +src/silx/resources/gui/icons/math-amplitude.svg +src/silx/resources/gui/icons/math-average.png +src/silx/resources/gui/icons/math-average.svg +src/silx/resources/gui/icons/math-derive.png +src/silx/resources/gui/icons/math-derive.svg +src/silx/resources/gui/icons/math-energy.png +src/silx/resources/gui/icons/math-energy.svg +src/silx/resources/gui/icons/math-fit.png +src/silx/resources/gui/icons/math-fit.svg +src/silx/resources/gui/icons/math-imaginary.png +src/silx/resources/gui/icons/math-imaginary.svg +src/silx/resources/gui/icons/math-mean.png +src/silx/resources/gui/icons/math-mean.svg +src/silx/resources/gui/icons/math-normalize.png +src/silx/resources/gui/icons/math-normalize.svg +src/silx/resources/gui/icons/math-peak-reset.png +src/silx/resources/gui/icons/math-peak-reset.svg +src/silx/resources/gui/icons/math-peak-search.png +src/silx/resources/gui/icons/math-peak-search.svg +src/silx/resources/gui/icons/math-peak.png +src/silx/resources/gui/icons/math-peak.svg +src/silx/resources/gui/icons/math-phase-color-log.png +src/silx/resources/gui/icons/math-phase-color-log.svg +src/silx/resources/gui/icons/math-phase-color.png +src/silx/resources/gui/icons/math-phase-color.svg +src/silx/resources/gui/icons/math-phase.png +src/silx/resources/gui/icons/math-phase.svg +src/silx/resources/gui/icons/math-real.png +src/silx/resources/gui/icons/math-real.svg +src/silx/resources/gui/icons/math-sigma.png +src/silx/resources/gui/icons/math-sigma.svg +src/silx/resources/gui/icons/math-smooth.png +src/silx/resources/gui/icons/math-smooth.svg +src/silx/resources/gui/icons/math-square-amplitude.png +src/silx/resources/gui/icons/math-square-amplitude.svg +src/silx/resources/gui/icons/math-substract.png +src/silx/resources/gui/icons/math-substract.svg +src/silx/resources/gui/icons/math-swap-sign.png +src/silx/resources/gui/icons/math-swap-sign.svg +src/silx/resources/gui/icons/math-ymin-to-zero.png +src/silx/resources/gui/icons/math-ymin-to-zero.svg +src/silx/resources/gui/icons/median-filter.png +src/silx/resources/gui/icons/median-filter.svg +src/silx/resources/gui/icons/next.png +src/silx/resources/gui/icons/next.svg +src/silx/resources/gui/icons/normal.png +src/silx/resources/gui/icons/normal.svg +src/silx/resources/gui/icons/nxdata-axis-add.png +src/silx/resources/gui/icons/nxdata-axis-add.svg +src/silx/resources/gui/icons/nxdata-axis-remove.png +src/silx/resources/gui/icons/nxdata-axis-remove.svg +src/silx/resources/gui/icons/nxdata-create.png +src/silx/resources/gui/icons/nxdata-create.svg +src/silx/resources/gui/icons/nxdata-remove.png +src/silx/resources/gui/icons/nxdata-remove.svg +src/silx/resources/gui/icons/pan.png +src/silx/resources/gui/icons/pan.svg +src/silx/resources/gui/icons/pixel-intensities.png +src/silx/resources/gui/icons/pixel-intensities.svg +src/silx/resources/gui/icons/plot-grid.png +src/silx/resources/gui/icons/plot-grid.svg +src/silx/resources/gui/icons/plot-roi-above.png +src/silx/resources/gui/icons/plot-roi-above.svg +src/silx/resources/gui/icons/plot-roi-below.png +src/silx/resources/gui/icons/plot-roi-below.svg +src/silx/resources/gui/icons/plot-roi-between.png +src/silx/resources/gui/icons/plot-roi-between.svg +src/silx/resources/gui/icons/plot-roi-reset.png +src/silx/resources/gui/icons/plot-roi-reset.svg +src/silx/resources/gui/icons/plot-roi.png +src/silx/resources/gui/icons/plot-roi.svg +src/silx/resources/gui/icons/plot-symbols.png +src/silx/resources/gui/icons/plot-symbols.svg +src/silx/resources/gui/icons/plot-toggle-points.png +src/silx/resources/gui/icons/plot-toggle-points.svg +src/silx/resources/gui/icons/plot-widget.png +src/silx/resources/gui/icons/plot-widget.svg +src/silx/resources/gui/icons/plot-window-image.png +src/silx/resources/gui/icons/plot-window-image.svg +src/silx/resources/gui/icons/plot-window.png +src/silx/resources/gui/icons/plot-window.svg +src/silx/resources/gui/icons/plot-xauto.png +src/silx/resources/gui/icons/plot-xauto.svg +src/silx/resources/gui/icons/plot-xlog.png +src/silx/resources/gui/icons/plot-xlog.svg +src/silx/resources/gui/icons/plot-yauto.png +src/silx/resources/gui/icons/plot-yauto.svg +src/silx/resources/gui/icons/plot-ydown.png +src/silx/resources/gui/icons/plot-ydown.svg +src/silx/resources/gui/icons/plot-ylog.png +src/silx/resources/gui/icons/plot-ylog.svg +src/silx/resources/gui/icons/plot-yup.png +src/silx/resources/gui/icons/plot-yup.svg +src/silx/resources/gui/icons/pointing-hand.png +src/silx/resources/gui/icons/pointing-hand.svg +src/silx/resources/gui/icons/previous.png +src/silx/resources/gui/icons/previous.svg +src/silx/resources/gui/icons/process-working.mng +src/silx/resources/gui/icons/profile-clear.png +src/silx/resources/gui/icons/profile-clear.svg +src/silx/resources/gui/icons/profile1D.png +src/silx/resources/gui/icons/profile1D.svg +src/silx/resources/gui/icons/profile2D.png +src/silx/resources/gui/icons/profile2D.svg +src/silx/resources/gui/icons/remove.png +src/silx/resources/gui/icons/remove.svg +src/silx/resources/gui/icons/rm.png +src/silx/resources/gui/icons/rm.svg +src/silx/resources/gui/icons/rotate-3d.png +src/silx/resources/gui/icons/rotate-3d.svg +src/silx/resources/gui/icons/rudder.png +src/silx/resources/gui/icons/rudder.svg +src/silx/resources/gui/icons/scale-auto.png +src/silx/resources/gui/icons/scale-auto.svg +src/silx/resources/gui/icons/scale-fixed.png +src/silx/resources/gui/icons/scale-fixed.svg +src/silx/resources/gui/icons/selected.png +src/silx/resources/gui/icons/selected.svg +src/silx/resources/gui/icons/shape-circle-solid.png +src/silx/resources/gui/icons/shape-circle-solid.svg +src/silx/resources/gui/icons/shape-circle.png +src/silx/resources/gui/icons/shape-circle.svg +src/silx/resources/gui/icons/shape-cross.png +src/silx/resources/gui/icons/shape-cross.svg +src/silx/resources/gui/icons/shape-diagonal-directed.png +src/silx/resources/gui/icons/shape-diagonal-directed.svg +src/silx/resources/gui/icons/shape-diagonal.png +src/silx/resources/gui/icons/shape-diagonal.svg +src/silx/resources/gui/icons/shape-ellipse-solid.png +src/silx/resources/gui/icons/shape-ellipse-solid.svg +src/silx/resources/gui/icons/shape-ellipse.png +src/silx/resources/gui/icons/shape-ellipse.svg +src/silx/resources/gui/icons/shape-horizontal.png +src/silx/resources/gui/icons/shape-horizontal.svg +src/silx/resources/gui/icons/shape-polygon.png +src/silx/resources/gui/icons/shape-polygon.svg +src/silx/resources/gui/icons/shape-rectangle.png +src/silx/resources/gui/icons/shape-rectangle.svg +src/silx/resources/gui/icons/shape-square.png +src/silx/resources/gui/icons/shape-square.svg +src/silx/resources/gui/icons/shape-vertical.png +src/silx/resources/gui/icons/shape-vertical.svg +src/silx/resources/gui/icons/side-histograms.png +src/silx/resources/gui/icons/side-histograms.svg +src/silx/resources/gui/icons/silx.png +src/silx/resources/gui/icons/silx.svg +src/silx/resources/gui/icons/slice-cross.png +src/silx/resources/gui/icons/slice-cross.svg +src/silx/resources/gui/icons/slice-horizontal.png +src/silx/resources/gui/icons/slice-horizontal.svg +src/silx/resources/gui/icons/slice-vertical.png +src/silx/resources/gui/icons/slice-vertical.svg +src/silx/resources/gui/icons/sliders-off.png +src/silx/resources/gui/icons/sliders-off.svg +src/silx/resources/gui/icons/sliders-on.png +src/silx/resources/gui/icons/sliders-on.svg +src/silx/resources/gui/icons/spec.png +src/silx/resources/gui/icons/spec.svg +src/silx/resources/gui/icons/stats-active-items.png +src/silx/resources/gui/icons/stats-active-items.svg +src/silx/resources/gui/icons/stats-visible-data.png +src/silx/resources/gui/icons/stats-visible-data.svg +src/silx/resources/gui/icons/stats-whole-data.png +src/silx/resources/gui/icons/stats-whole-data.svg +src/silx/resources/gui/icons/stats-whole-items.png +src/silx/resources/gui/icons/stats-whole-items.svg +src/silx/resources/gui/icons/tree-collapse-all.png +src/silx/resources/gui/icons/tree-collapse-all.svg +src/silx/resources/gui/icons/tree-expand-all.png +src/silx/resources/gui/icons/tree-expand-all.svg +src/silx/resources/gui/icons/tree-sort.png +src/silx/resources/gui/icons/tree-sort.svg +src/silx/resources/gui/icons/view-1d.png +src/silx/resources/gui/icons/view-1d.svg +src/silx/resources/gui/icons/view-2d-stack.png +src/silx/resources/gui/icons/view-2d-stack.svg +src/silx/resources/gui/icons/view-2d.png +src/silx/resources/gui/icons/view-2d.svg +src/silx/resources/gui/icons/view-3d.png +src/silx/resources/gui/icons/view-3d.svg +src/silx/resources/gui/icons/view-fullscreen.png +src/silx/resources/gui/icons/view-fullscreen.svg +src/silx/resources/gui/icons/view-hdf5.png +src/silx/resources/gui/icons/view-hdf5.svg +src/silx/resources/gui/icons/view-nexus.png +src/silx/resources/gui/icons/view-nexus.svg +src/silx/resources/gui/icons/view-nofullscreen.png +src/silx/resources/gui/icons/view-nofullscreen.svg +src/silx/resources/gui/icons/view-raw.png +src/silx/resources/gui/icons/view-raw.svg +src/silx/resources/gui/icons/view-refresh.png +src/silx/resources/gui/icons/view-refresh.svg +src/silx/resources/gui/icons/view-text.png +src/silx/resources/gui/icons/view-text.svg +src/silx/resources/gui/icons/window-new.png +src/silx/resources/gui/icons/window-new.svg +src/silx/resources/gui/icons/zoom-back.png +src/silx/resources/gui/icons/zoom-back.svg +src/silx/resources/gui/icons/zoom-in.png +src/silx/resources/gui/icons/zoom-in.svg +src/silx/resources/gui/icons/zoom-original.png +src/silx/resources/gui/icons/zoom-original.svg +src/silx/resources/gui/icons/zoom-out.png +src/silx/resources/gui/icons/zoom-out.svg +src/silx/resources/gui/icons/zoom.png +src/silx/resources/gui/icons/zoom.svg +src/silx/resources/gui/icons/process-working/00.png +src/silx/resources/gui/icons/process-working/01.png +src/silx/resources/gui/icons/process-working/02.png +src/silx/resources/gui/icons/process-working/03.png +src/silx/resources/gui/icons/process-working/04.png +src/silx/resources/gui/icons/process-working/05.png +src/silx/resources/gui/icons/process-working/06.png +src/silx/resources/gui/icons/process-working/07.png +src/silx/resources/gui/icons/process-working/08.png +src/silx/resources/gui/icons/process-working/09.png +src/silx/resources/gui/icons/process-working/10.png +src/silx/resources/gui/icons/process-working/11.png +src/silx/resources/gui/icons/process-working/12.png +src/silx/resources/gui/icons/process-working/13.png +src/silx/resources/gui/icons/process-working/14.png +src/silx/resources/gui/icons/process-working/15.png +src/silx/resources/gui/icons/process-working/16.png +src/silx/resources/gui/icons/process-working/17.png +src/silx/resources/gui/icons/process-working/18.png +src/silx/resources/gui/icons/process-working/19.png +src/silx/resources/gui/icons/process-working/20.png +src/silx/resources/gui/icons/process-working/21.png +src/silx/resources/gui/icons/process-working/22.png +src/silx/resources/gui/icons/process-working/23.png +src/silx/resources/gui/icons/process-working/24.png +src/silx/resources/gui/icons/process-working/25.png +src/silx/resources/gui/icons/process-working/26.png +src/silx/resources/gui/icons/process-working/27.png +src/silx/resources/gui/icons/process-working/28.png +src/silx/resources/gui/icons/process-working/29.png +src/silx/resources/gui/icons/process-working/30.png +src/silx/resources/gui/logo/silx.png +src/silx/resources/gui/logo/silx.svg +src/silx/resources/opencl/addition.cl +src/silx/resources/opencl/array_utils.cl +src/silx/resources/opencl/backproj.cl +src/silx/resources/opencl/backproj_helper.cl +src/silx/resources/opencl/bitonic.cl +src/silx/resources/opencl/convolution.cl +src/silx/resources/opencl/convolution_textures.cl +src/silx/resources/opencl/doubleword.cl +src/silx/resources/opencl/kahan.cl +src/silx/resources/opencl/linalg.cl +src/silx/resources/opencl/medfilt.cl +src/silx/resources/opencl/preprocess.cl +src/silx/resources/opencl/proj.cl +src/silx/resources/opencl/sparse.cl +src/silx/resources/opencl/statistics.cl +src/silx/resources/opencl/codec/byte_offset.cl +src/silx/resources/opencl/image/cast.cl +src/silx/resources/opencl/image/histogram.cl +src/silx/resources/opencl/image/map.cl +src/silx/resources/opencl/image/max_min.cl +src/silx/resources/opencl/sift/addition.cl +src/silx/resources/opencl/sift/algebra.cl +src/silx/resources/opencl/sift/convolution.cl +src/silx/resources/opencl/sift/descriptor_cpu.cl +src/silx/resources/opencl/sift/descriptor_gpu1.cl +src/silx/resources/opencl/sift/descriptor_gpu2.cl +src/silx/resources/opencl/sift/gaussian.cl +src/silx/resources/opencl/sift/image.cl +src/silx/resources/opencl/sift/interpolation.cl +src/silx/resources/opencl/sift/matching_cpu.cl +src/silx/resources/opencl/sift/matching_gpu.cl +src/silx/resources/opencl/sift/memset.cl +src/silx/resources/opencl/sift/orientation_cpu.cl +src/silx/resources/opencl/sift/orientation_gpu.cl +src/silx/resources/opencl/sift/preprocess.cl +src/silx/resources/opencl/sift/reductions.cl +src/silx/resources/opencl/sift/sift.cl +src/silx/resources/opencl/sift/transform.cl +src/silx/sx/__init__.py +src/silx/sx/_plot.py +src/silx/sx/_plot3d.py +src/silx/test/__init__.py +src/silx/test/test_resources.py +src/silx/test/test_sx.py +src/silx/test/test_version.py +src/silx/test/utils.py +src/silx/third_party/EdfFile.py +src/silx/third_party/TiffIO.py +src/silx/third_party/__init__.py +src/silx/third_party/scipy_spatial.py +src/silx/third_party/_local/__init__.py +src/silx/third_party/_local/scipy_spatial/__init__.py +src/silx/third_party/_local/scipy_spatial/qhull.pxd +src/silx/third_party/_local/scipy_spatial/qhull.pyx +src/silx/third_party/_local/scipy_spatial/qhull_misc.h +src/silx/third_party/_local/scipy_spatial/setlist.pxd +src/silx/third_party/_local/scipy_spatial/qhull/COPYING.txt +src/silx/third_party/_local/scipy_spatial/qhull/src/geom2_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/geom_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/geom_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/global_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/io_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/io_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/libqhull_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/libqhull_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/mem_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/mem_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/merge_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/merge_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/poly2_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/poly_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/poly_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/qhull_ra.h +src/silx/third_party/_local/scipy_spatial/qhull/src/qset_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/qset_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/random_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/random_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/rboxlib_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/stat_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/stat_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/user_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/user_r.h +src/silx/third_party/_local/scipy_spatial/qhull/src/usermem_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/userprintf_r.c +src/silx/third_party/_local/scipy_spatial/qhull/src/userprintf_rbox_r.c +src/silx/utils/ExternalResources.py +src/silx/utils/__init__.py +src/silx/utils/_have_openmp.pxd +src/silx/utils/array_like.py +src/silx/utils/debug.py +src/silx/utils/deprecation.py +src/silx/utils/enum.py +src/silx/utils/exceptions.py +src/silx/utils/files.py +src/silx/utils/html.py +src/silx/utils/launcher.py +src/silx/utils/number.py +src/silx/utils/property.py +src/silx/utils/proxy.py +src/silx/utils/retry.py +src/silx/utils/testutils.py +src/silx/utils/weakref.py +src/silx/utils/include/silx_store_openmp.h +src/silx/utils/test/__init__.py +src/silx/utils/test/test_array_like.py +src/silx/utils/test/test_debug.py +src/silx/utils/test/test_deprecation.py +src/silx/utils/test/test_enum.py +src/silx/utils/test/test_external_resources.py +src/silx/utils/test/test_launcher.py +src/silx/utils/test/test_launcher_command.py +src/silx/utils/test/test_number.py +src/silx/utils/test/test_proxy.py +src/silx/utils/test/test_retry.py +src/silx/utils/test/test_testutils.py +src/silx/utils/test/test_weakref.py
\ No newline at end of file diff --git a/src/silx.egg-info/dependency_links.txt b/src/silx.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/silx.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/silx.egg-info/entry_points.txt b/src/silx.egg-info/entry_points.txt new file mode 100644 index 0000000..898d662 --- /dev/null +++ b/src/silx.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +silx = silx.__main__:main + diff --git a/src/silx.egg-info/not-zip-safe b/src/silx.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/silx.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/src/silx.egg-info/requires.txt b/src/silx.egg-info/requires.txt new file mode 100644 index 0000000..11d2418 --- /dev/null +++ b/src/silx.egg-info/requires.txt @@ -0,0 +1,20 @@ +numpy>=1.12.0 +setuptools +h5py +fabio>=0.9 + +[full] +pyopencl +Mako +qtconsole +matplotlib>=1.2.0 +PyOpenGL +python-dateutil +PyQt5 +hdf5plugin +scipy +Pillow + +[test] +pytest +pytest-xvfb diff --git a/src/silx.egg-info/top_level.txt b/src/silx.egg-info/top_level.txt new file mode 100644 index 0000000..dbcf435 --- /dev/null +++ b/src/silx.egg-info/top_level.txt @@ -0,0 +1 @@ +silx diff --git a/src/silx/__init__.py b/src/silx/__init__.py index 0ad0357..c8d6e5b 100644 --- a/src/silx/__init__.py +++ b/src/silx/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility @@ -35,8 +34,6 @@ See silx documentation: http://www.silx.org/doc/silx/latest/ """ -from __future__ import absolute_import, print_function, division - __authors__ = ["Jérôme Kieffer"] __license__ = "MIT" __date__ = "26/04/2018" diff --git a/src/silx/__main__.py b/src/silx/__main__.py index f832a09..0f8727c 100644 --- a/src/silx/__main__.py +++ b/src/silx/__main__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/_config.py b/src/silx/_config.py index fb0e409..5d7b445 100644 --- a/src/silx/_config.py +++ b/src/silx/_config.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2019 European Synchrotron Radiation Facility diff --git a/src/silx/_version.py b/src/silx/_version.py index feb2639..511e823 100644 --- a/src/silx/_version.py +++ b/src/silx/_version.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2015-2020 European Synchrotron Radiation Facility +# Copyright (c) 2015-2022 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 @@ -49,7 +48,6 @@ Thus 2.1.0a3 is hexversion 0x020100a3. """ -from __future__ import absolute_import, print_function, division __authors__ = ["Jérôme Kieffer"] __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" @@ -71,7 +69,7 @@ PRERELEASE_NORMALIZED_NAME = {"dev": "a", "candidate": "rc"} MAJOR = 1 -MINOR = 0 +MINOR = 1 MICRO = 0 RELEV = "final" # <16 SERIAL = 0 # <16 diff --git a/src/silx/app/__init__.py b/src/silx/app/__init__.py index 3af680c..4c0bc00 100644 --- a/src/silx/app/__init__.py +++ b/src/silx/app/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/app/convert.py b/src/silx/app/convert.py index 43baf7e..78c1ebf 100644 --- a/src/silx/app/convert.py +++ b/src/silx/app/convert.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/app/setup.py b/src/silx/app/setup.py deleted file mode 100644 index 85c3662..0000000 --- a/src/silx/app/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "23/04/2018" - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('app', parent_package, top_path) - config.add_subpackage('test') - config.add_subpackage('view') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/app/test/__init__.py b/src/silx/app/test/__init__.py index 7790ee5..1d8207b 100644 --- a/src/silx/app/test/__init__.py +++ b/src/silx/app/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/app/test/test_convert.py b/src/silx/app/test/test_convert.py index 2148db5..f3ca269 100644 --- a/src/silx/app/test/test_convert.py +++ b/src/silx/app/test/test_convert.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/app/test_.py b/src/silx/app/test_.py index 2b6bdf8..9696eb2 100644 --- a/src/silx/app/test_.py +++ b/src/silx/app/test_.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/app/view/About.py b/src/silx/app/view/About.py index 85f1450..2af7ed4 100644 --- a/src/silx/app/view/About.py +++ b/src/silx/app/view/About.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -25,7 +24,7 @@ __authors__ = ["V. Valls"] __license__ = "MIT" -__date__ = "05/07/2018" +__date__ = "18/01/2022" import os import sys @@ -233,7 +232,7 @@ class About(qt.QDialog): hardLimit = min(screenSize.width() - 480, 1000) if screenSize.width() <= 1024: hardLimit = screenSize.width() - softLimit = min(screenSize.width() / 2, 420) + softLimit = min(screenSize.width() // 2, 420) layoutMinimumSize = self.layout().totalMinimumSize() width = layoutMinimumSize.width() diff --git a/src/silx/app/view/ApplicationContext.py b/src/silx/app/view/ApplicationContext.py index 324f3b8..30dad7d 100644 --- a/src/silx/app/view/ApplicationContext.py +++ b/src/silx/app/view/ApplicationContext.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/app/view/CustomNxdataWidget.py b/src/silx/app/view/CustomNxdataWidget.py index 8c6cd39..3c79c0d 100644 --- a/src/silx/app/view/CustomNxdataWidget.py +++ b/src/silx/app/view/CustomNxdataWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/app/view/DataPanel.py b/src/silx/app/view/DataPanel.py index 5d87381..d4a0e63 100644 --- a/src/silx/app/view/DataPanel.py +++ b/src/silx/app/view/DataPanel.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018 European Synchrotron Radiation Facility # diff --git a/src/silx/app/view/Viewer.py b/src/silx/app/view/Viewer.py index 7e5e4c9..d9ecf6a 100644 --- a/src/silx/app/view/Viewer.py +++ b/src/silx/app/view/Viewer.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -32,6 +31,7 @@ import os import collections import logging import functools +from typing import Optional import silx.io.nxdata from silx.gui import qt @@ -169,7 +169,7 @@ class Viewer(qt.QMainWindow): # Another shortcut for refresh action = qt.QAction(toolbar) - action.setShortcut(qt.QKeySequence(qt.Qt.ControlModifier + qt.Qt.Key_R)) + action.setShortcut(qt.QKeySequence(qt.Qt.CTRL | qt.Qt.Key_R)) treeView.addAction(action) action.triggered.connect(self.__refreshSelected) @@ -189,7 +189,7 @@ class Viewer(qt.QMainWindow): action.setText("Expand all") action.setToolTip("Expand all selected items") action.triggered.connect(self.__expandAllSelected) - action.setShortcut(qt.QKeySequence(qt.Qt.ControlModifier + qt.Qt.Key_Plus)) + action.setShortcut(qt.QKeySequence(qt.Qt.CTRL | qt.Qt.Key_Plus)) toolbar.addAction(action) treeView.addAction(action) self.__expandAllAction = action @@ -199,7 +199,7 @@ class Viewer(qt.QMainWindow): action.setText("Collapse all") action.setToolTip("Collapse all selected items") action.triggered.connect(self.__collapseAllSelected) - action.setShortcut(qt.QKeySequence(qt.Qt.ControlModifier + qt.Qt.Key_Minus)) + action.setShortcut(qt.QKeySequence(qt.Qt.CTRL | qt.Qt.Key_Minus)) toolbar.addAction(action) treeView.addAction(action) self.__collapseAllAction = action @@ -262,12 +262,11 @@ class Viewer(qt.QMainWindow): indexes = selection.selectedIndexes() selectedItems = [] model = self.__treeview.model() - h5files = set([]) + h5files = [] while len(indexes) > 0: index = indexes.pop(0) if index.column() != 0: continue - h5 = model.data(index, role=silx.gui.hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE) rootIndex = index # Reach the root of the tree while rootIndex.parent().isValid(): @@ -275,15 +274,17 @@ class Viewer(qt.QMainWindow): rootRow = rootIndex.row() relativePath = self.__getRelativePath(model, rootIndex, index) selectedItems.append((rootRow, relativePath)) - h5files.add(h5.file) + h5 = model.data(rootIndex, role=silx.gui.hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE) + item = model.data(rootIndex, role=silx.gui.hdf5.Hdf5TreeModel.H5PY_ITEM_ROLE) + h5files.append((h5, item._openedPath)) if len(h5files) == 0: qt.QApplication.restoreOverrideCursor() return model = self.__treeview.findHdf5TreeModel() - for h5 in h5files: - self.__synchronizeH5pyObject(h5) + for h5, filename in h5files: + self.__synchronizeH5pyObject(h5, filename) model = self.__treeview.model() itemSelection = qt.QItemSelection() @@ -298,14 +299,15 @@ class Viewer(qt.QMainWindow): qt.QApplication.restoreOverrideCursor() - def __synchronizeH5pyObject(self, h5): + def __synchronizeH5pyObject(self, h5, filename: Optional[str] = None): model = self.__treeview.findHdf5TreeModel() # This is buggy right now while h5py do not allow to close a file # while references are still used. # FIXME: The architecture have to be reworked to support this feature. # model.synchronizeH5pyObject(h5) - filename = h5.filename + if filename is None: + filename = f"{h5.file.filename}::{h5.name}" row = model.h5pyObjectRow(h5) index = self.__treeview.model().index(row, 0, qt.QModelIndex()) paths = self.__getPathFromExpandedNodes(self.__treeview, index) diff --git a/src/silx/app/view/__init__.py b/src/silx/app/view/__init__.py index 229c44e..97c64ef 100644 --- a/src/silx/app/view/__init__.py +++ b/src/silx/app/view/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/app/view/main.py b/src/silx/app/view/main.py index dbc6a2b..c37b8aa 100644 --- a/src/silx/app/view/main.py +++ b/src/silx/app/view/main.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -28,10 +27,12 @@ __license__ = "MIT" __date__ = "17/01/2019" import argparse +import glob import logging import os import signal import sys +from typing import Generator, Iterable _logger = logging.getLogger(__name__) @@ -71,6 +72,40 @@ def createParser(): return parser +def filesArgToUrls(filenames: Iterable[str]) -> Generator[object, None, None]: + """Expand filenames and HDF5 data path in files input argument""" + # Imports here so they are performed after setting HDF5_USE_FILE_LOCKING and logging level + import silx.io + from silx.io.utils import match + from silx.io.url import DataUrl + import silx.utils.files + + for filename in filenames: + url = DataUrl(filename) + + for file_path in sorted(silx.utils.files.expand_filenames([url.file_path()])): + if url.data_path() is not None and glob.has_magic(url.data_path()): + try: + with silx.io.open(file_path) as f: + data_paths = list(match(f, url.data_path())) + except BaseException as e: + _logger.error( + f"Error searching HDF5 path pattern '{url.data_path()}' in file '{file_path}': Ignored") + _logger.error(e.args[0]) + _logger.debug("Backtrace", exc_info=True) + continue + else: + data_paths = [url.data_path()] + + for data_path in data_paths: + yield DataUrl( + file_path=file_path, + data_path=data_path, + data_slice=url.data_slice(), + scheme=url.scheme(), + ) + + def createWindow(parent, settings): from .Viewer import Viewer window = Viewer(parent=None, settings=settings) @@ -115,7 +150,6 @@ def mainQt(options): import h5py import silx - import silx.utils.files from silx.gui import qt # Make sure matplotlib is configured # Needed for Debian 8: compatibility between Qt4/Qt5 and old matplotlib @@ -152,13 +186,11 @@ def mainQt(options): # It have to be done after the settings (after the Viewer creation) silx.config.DEFAULT_PLOT_BACKEND = "opengl" - # NOTE: under Windows, cmd does not convert `*.tif` into existing files - options.files = silx.utils.files.expand_filenames(options.files) - for filename in options.files: + for url in filesArgToUrls(options.files): # TODO: Would be nice to add a process widget and a cancel button try: - window.appendFile(filename) + window.appendFile(url.path()) except IOError as e: _logger.error(e.args[0]) _logger.debug("Backtrace", exc_info=True) diff --git a/src/silx/app/view/setup.py b/src/silx/app/view/setup.py deleted file mode 100644 index fa076cb..0000000 --- a/src/silx/app/view/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "06/06/2018" - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('view', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/app/view/test/__init__.py b/src/silx/app/view/test/__init__.py index 7790ee5..1d8207b 100644 --- a/src/silx/app/view/test/__init__.py +++ b/src/silx/app/view/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/app/view/test/test_launcher.py b/src/silx/app/view/test/test_launcher.py index 4f7aaa5..8ccf4af 100644 --- a/src/silx/app/view/test/test_launcher.py +++ b/src/silx/app/view/test/test_launcher.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/app/view/test/test_view.py b/src/silx/app/view/test/test_view.py index e236e42..362995a 100644 --- a/src/silx/app/view/test/test_view.py +++ b/src/silx/app/view/test/test_view.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/app/view/utils.py b/src/silx/app/view/utils.py index 80167c8..6a980e9 100644 --- a/src/silx/app/view/utils.py +++ b/src/silx/app/view/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018 European Synchrotron Radiation Facility # diff --git a/src/silx/conftest.py b/src/silx/conftest.py index 53b3edc..bec67c0 100644 --- a/src/silx/conftest.py +++ b/src/silx/conftest.py @@ -18,13 +18,16 @@ def _set_qt_binding(binding): elif binding == "pyside6": logger.info("Force using PySide6") import PySide6.QtCore # noqa + elif binding == "pyqt6": + logger.info("Force using PyQt6") + import PyQt6.QtCore # noqa else: raise ValueError("Qt binding '%s' is unknown" % binding) def pytest_addoption(parser): parser.addoption("--qt-binding", type=str, default=None, dest="qt_binding", - help="Force using a Qt binding: 'PyQt5', 'PySide2', 'PySide6'") + help="Force using a Qt binding: 'PyQt5', 'PySide2', 'PySide6', 'PyQt6'") parser.addoption("--no-gui", dest="gui", default=True, action="store_false", help="Disable the test of the graphical use interface") diff --git a/src/silx/gui/__init__.py b/src/silx/gui/__init__.py index b796e20..31bb38e 100644 --- a/src/silx/gui/__init__.py +++ b/src/silx/gui/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/Context.py b/src/silx/gui/_glutils/Context.py index c62dbb9..d2ddaa3 100644 --- a/src/silx/gui/_glutils/Context.py +++ b/src/silx/gui/_glutils/Context.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/FramebufferTexture.py b/src/silx/gui/_glutils/FramebufferTexture.py index d12a6e0..75db264 100644 --- a/src/silx/gui/_glutils/FramebufferTexture.py +++ b/src/silx/gui/_glutils/FramebufferTexture.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/OpenGLWidget.py b/src/silx/gui/_glutils/OpenGLWidget.py index 2ca4649..d35bb73 100644 --- a/src/silx/gui/_glutils/OpenGLWidget.py +++ b/src/silx/gui/_glutils/OpenGLWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -45,7 +44,7 @@ _logger = logging.getLogger(__name__) if not hasattr(qt, 'QOpenGLWidget') and not hasattr(qt, 'QGLWidget'): - OpenGLWidget = None + _OpenGLWidget = None else: if hasattr(qt, 'QOpenGLWidget'): # PyQt>=5.4 @@ -70,7 +69,7 @@ else: depthBufferSize=24, stencilBufferSize=8, version=(2, 0), - f=qt.Qt.WindowFlags()): + f=qt.Qt.Widget): # True if using QGLWidget, False if using QOpenGLWidget self.__legacy = not hasattr(qt, 'QOpenGLWidget') @@ -262,7 +261,7 @@ class OpenGLWidget(qt.QWidget): depthBufferSize=24, stencilBufferSize=8, version=(2, 0), - f=qt.Qt.WindowFlags()): + f=qt.Qt.Widget): super(OpenGLWidget, self).__init__(parent, f) layout = qt.QHBoxLayout(self) diff --git a/src/silx/gui/_glutils/Program.py b/src/silx/gui/_glutils/Program.py index 87eec5f..d61c07d 100644 --- a/src/silx/gui/_glutils/Program.py +++ b/src/silx/gui/_glutils/Program.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/Texture.py b/src/silx/gui/_glutils/Texture.py index c72135a..76bdcd8 100644 --- a/src/silx/gui/_glutils/Texture.py +++ b/src/silx/gui/_glutils/Texture.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/VertexBuffer.py b/src/silx/gui/_glutils/VertexBuffer.py index b74b748..65fff86 100644 --- a/src/silx/gui/_glutils/VertexBuffer.py +++ b/src/silx/gui/_glutils/VertexBuffer.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/__init__.py b/src/silx/gui/_glutils/__init__.py index e88affd..a7a4bee 100644 --- a/src/silx/gui/_glutils/__init__.py +++ b/src/silx/gui/_glutils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/_glutils/font.py b/src/silx/gui/_glutils/font.py index 3ea474d..bee9745 100644 --- a/src/silx/gui/_glutils/font.py +++ b/src/silx/gui/_glutils/font.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -32,8 +31,13 @@ __date__ = "13/10/2016" import logging import numpy -from ..utils.image import convertQImageToArray from .. import qt +from ..utils.image import convertQImageToArray + +try: + from ..utils.matplotlib import rasterMathText +except ImportError: + rasterMathText = None _logger = logging.getLogger(__name__) @@ -66,11 +70,7 @@ ULTRA_BLACK = 99 """Thickest characters: Maximum font weight""" -def rasterText(text, font, - size=-1, - weight=-1, - italic=False, - devicePixelRatio=1.0): +def rasterTextQt(text, font, size=-1, weight=-1, italic=False, devicePixelRatio=1.0): """Raster text using Qt. It supports multiple lines. @@ -95,7 +95,7 @@ def rasterText(text, font, """ if not text: _logger.info("Trying to raster empty text, replaced by white space") - text = ' ' # Replace empty text by white space to produce an image + text = " " # Replace empty text by white space to produce an image if not isinstance(font, qt.QFont): font = qt.QFont(font, size, weight, italic) @@ -107,7 +107,8 @@ def rasterText(text, font, painter.setPen(qt.Qt.white) painter.setFont(font) bounds = painter.boundingRect( - qt.QRect(0, 0, 4096, 4096), qt.Qt.TextExpandTabs, text) + qt.QRect(0, 0, 4096, 4096), qt.Qt.TextExpandTabs, text + ) painter.end() metrics = qt.QFontMetrics(font) @@ -123,9 +124,9 @@ def rasterText(text, font, width = bounds.width() * devicePixelRatio + 2 # align line size to 32 bits to ease conversion to numpy array width = 4 * ((width + 3) // 4) - image = qt.QImage(int(width), - int(bounds.height() * devicePixelRatio + 2), - qt.QImage.Format_RGB888) + image = qt.QImage( + int(width), int(bounds.height() * devicePixelRatio + 2), qt.QImage.Format_RGB888 + ) image.setDevicePixelRatio(devicePixelRatio) # TODO if Qt5 use Format_Grayscale8 instead @@ -144,13 +145,45 @@ def rasterText(text, font, # RGB to R array = numpy.ascontiguousarray(array[:, :, 0]) - # Remove leading and trailing empty columns but one on each side - column_cumsum = numpy.cumsum(numpy.sum(array, axis=0)) - array = array[:, column_cumsum.argmin():column_cumsum.argmax() + 2] + # Remove leading and trailing empty columns/rows but one on each side + filled_rows = numpy.nonzero(numpy.sum(array, axis=1))[0] + filled_columns = numpy.nonzero(numpy.sum(array, axis=0))[0] + if len(filled_rows) == 0 or len(filled_columns) == 0: + return array, metrics.ascent() - # Remove leading and trailing empty rows but one on each side - row_cumsum = numpy.cumsum(numpy.sum(array, axis=1)) - min_row = row_cumsum.argmin() - array = array[min_row:row_cumsum.argmax() + 2, :] + min_row = max(0, filled_rows[0] - 1) + array = array[ + min_row : filled_rows[-1] + 2, + max(0, filled_columns[0] - 1) : filled_columns[-1] + 2, + ] return array, metrics.ascent() - min_row + + +def rasterText(text, font, size=-1, weight=-1, italic=False, devicePixelRatio=1.0): + """Raster text using Qt or matplotlib if there may be math syntax. + + It supports multiple lines. + + :param str text: The text to raster + :param font: Font name or QFont to use + :type font: str or :class:`QFont` + :param int size: + Font size in points + Used only if font is given as name. + :param int weight: + Font weight in [0, 99], see QFont.Weight. + Used only if font is given as name. + :param bool italic: + True for italic font (default: False). + Used only if font is given as name. + :param float devicePixelRatio: + The current ratio between device and device-independent pixel + (default: 1.0) + :return: Corresponding image in gray scale and baseline offset from top + :rtype: (HxW numpy.ndarray of uint8, int) + """ + if rasterMathText is not None and text.count("$") >= 2: + return rasterMathText(text, font, size, weight, italic, devicePixelRatio) + else: + return rasterTextQt(text, font, size, weight, italic, devicePixelRatio) diff --git a/src/silx/gui/_glutils/gl.py b/src/silx/gui/_glutils/gl.py index 608d9ce..d33cf49 100644 --- a/src/silx/gui/_glutils/gl.py +++ b/src/silx/gui/_glutils/gl.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2017 European Synchrotron Radiation Facility @@ -64,7 +63,7 @@ except NameError: GLchar = c_char -def testGL(): +def testGL() -> bool: """Test if required OpenGL version and extensions are available. This MUST be run with an active OpenGL context. @@ -72,18 +71,20 @@ def testGL(): version = glGetString(GL_VERSION).split()[0] # get version number major, minor = int(version[0]), int(version[2]) if major < 2 or (major == 2 and minor < 1): - raise RuntimeError( - "Requires at least OpenGL version 2.1, running with %s" % version) + _logger.error("OpenGL version >=2.1 required, running with %s" % version) + return False from OpenGL.GL.ARB.framebuffer_object import glInitFramebufferObjectARB from OpenGL.GL.ARB.texture_rg import glInitTextureRgARB if not glInitFramebufferObjectARB(): - raise RuntimeError( - "OpenGL GL_ARB_framebuffer_object extension required !") + _logger.error("OpenGL GL_ARB_framebuffer_object extension required!") + return False if not glInitTextureRgARB(): - raise RuntimeError("OpenGL GL_ARB_texture_rg extension required !") + _logger.error("OpenGL GL_ARB_texture_rg extension required!") + return False + return True # Additional setup diff --git a/src/silx/gui/_glutils/utils.py b/src/silx/gui/_glutils/utils.py index 5886599..49b431a 100644 --- a/src/silx/gui/_glutils/utils.py +++ b/src/silx/gui/_glutils/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/colors.py b/src/silx/gui/colors.py index 12046cf..4a5f278 100755 --- a/src/silx/gui/colors.py +++ b/src/silx/gui/colors.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides API to manage colors. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent", "H.Payno"] __license__ = "MIT" __date__ = "29/01/2019" diff --git a/src/silx/gui/console.py b/src/silx/gui/console.py index 953b6a1..c66d44a 100644 --- a/src/silx/gui/console.py +++ b/src/silx/gui/console.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -182,13 +181,6 @@ class IPythonDockWidget(qt.QDockWidget): if available_vars is not None: self.ipyconsole.pushVariables(available_vars) - def showEvent(self, event): - """Make sure this widget is raised when it is shown - (when it is first created as a tab in PlotWindow or when it is shown - again after hiding). - """ - self.raise_() - def main(): """Run a Qt app with an IPython console""" diff --git a/src/silx/gui/data/ArrayTableModel.py b/src/silx/gui/data/ArrayTableModel.py index 23b0bb2..00cc235 100644 --- a/src/silx/gui/data/ArrayTableModel.py +++ b/src/silx/gui/data/ArrayTableModel.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -26,7 +25,6 @@ This module defines a data model for displaying and editing arrays of any number of dimensions in a table view. """ -from __future__ import division import numpy import logging from silx.gui import qt @@ -34,7 +32,7 @@ from silx.gui.data.TextFormatter import TextFormatter __authors__ = ["V.A. Sole"] __license__ = "MIT" -__date__ = "27/09/2017" +__date__ = "18/01/2022" _logger = logging.getLogger(__name__) @@ -75,7 +73,7 @@ class ArrayTableModel(qt.QAbstractTableModel): of :meth:`setPerspective`. """ - MAX_NUMBER_OF_SECTIONS = 10e6 + MAX_NUMBER_OF_SECTIONS = 10000000 """Maximum number of displayed rows and columns""" def __init__(self, parent=None, data=None, perspective=None): @@ -235,7 +233,7 @@ class ArrayTableModel(qt.QAbstractTableModel): selection = self._getIndexTuple(row, column) - if role == qt.Qt.DisplayRole: + if role == qt.Qt.DisplayRole or role == qt.Qt.EditRole: return self._formatter.toString(self._array[selection], self._array.dtype) if role == qt.Qt.BackgroundRole and self._bgcolors is not None: diff --git a/src/silx/gui/data/ArrayTableWidget.py b/src/silx/gui/data/ArrayTableWidget.py index baef5f4..2f7762d 100644 --- a/src/silx/gui/data/ArrayTableWidget.py +++ b/src/silx/gui/data/ArrayTableWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -30,7 +29,6 @@ sliders. The widget uses a TableView that relies on a custom abstract item model: :class:`silx.gui.data.ArrayTableModel`. """ -from __future__ import division import sys from silx.gui import qt diff --git a/src/silx/gui/data/DataViewer.py b/src/silx/gui/data/DataViewer.py index 2e51439..2c93c65 100644 --- a/src/silx/gui/data/DataViewer.py +++ b/src/silx/gui/data/DataViewer.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2019 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -25,7 +24,6 @@ """This module defines a widget designed to display data using the most adapted view from the ones provided by silx. """ -from __future__ import division import logging import os.path @@ -83,6 +81,14 @@ class DataViewer(qt.QFrame): dataChanged = qt.Signal() """Emitted when the data changes""" + selectionChanged = qt.Signal(object, object) + """Emitted when the data selection changes. + + It provides: + - the slicing as a tuple of slice or None. + - the permutation as a tuple of int or None. + """ + currentAvailableViewsChanged = qt.Signal() """Emitted when the current available views (which support the current data) change""" @@ -118,6 +124,7 @@ class DataViewer(qt.QFrame): self.__useAxisSelection = False self.__userSelectedView = None self.__hooks = None + self.__previousSelection = DataSelection(None, None, None, None) self.__views = [] self.__index = {} @@ -279,6 +286,13 @@ class DataViewer(qt.QFrame): def __setDataInView(self): self.__currentView.setData(self.__displayedData) self.__currentView.setDataSelection(self.__displayedSelection) + # Emit signal only when selection has changed + if (self.__previousSelection.slice != self.__displayedSelection.slice or + self.__previousSelection.permutation != self.__displayedSelection.permutation + ): + self.selectionChanged.emit( + self.__displayedSelection.slice, self.__displayedSelection.permutation) + self.__previousSelection = self.__displayedSelection def setDisplayedView(self, view): """Set the displayed view. diff --git a/src/silx/gui/data/DataViewerFrame.py b/src/silx/gui/data/DataViewerFrame.py index 9bfb95b..912ca1c 100644 --- a/src/silx/gui/data/DataViewerFrame.py +++ b/src/silx/gui/data/DataViewerFrame.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -60,6 +59,14 @@ class DataViewerFrame(qt.QWidget): dataChanged = qt.Signal() """Emitted when the data changes""" + selectionChanged = qt.Signal(object, object) + """Emitted when the data selection changes. + + It provides: + - the slicing as a tuple of slice or None. + - the permutation as a tuple of int or None. + """ + def __init__(self, parent=None): """ Constructor @@ -104,6 +111,7 @@ class DataViewerFrame(qt.QWidget): self.__dataViewer.dataChanged.connect(self.__dataChanged) self.__dataViewer.displayedViewChanged.connect(self.__displayedViewChanged) + self.__dataViewer.selectionChanged.connect(self.__selectionChanged) def __dataChanged(self): """Called when the data is changed""" @@ -113,6 +121,10 @@ class DataViewerFrame(qt.QWidget): """Called when the displayed view changes""" self.displayedViewChanged.emit(view) + def __selectionChanged(self, slices, permutation): + """Called when the data selection has changed""" + self.selectionChanged.emit(slices, permutation) + def setGlobalHooks(self, hooks): """Set a data view hooks for all the views diff --git a/src/silx/gui/data/DataViewerSelector.py b/src/silx/gui/data/DataViewerSelector.py index a1e9947..d67908e 100644 --- a/src/silx/gui/data/DataViewerSelector.py +++ b/src/silx/gui/data/DataViewerSelector.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility @@ -25,7 +24,6 @@ """This module defines a widget to be able to select the available view of the DataViewer. """ -from __future__ import division __authors__ = ["V. Valls"] __license__ = "MIT" diff --git a/src/silx/gui/data/DataViews.py b/src/silx/gui/data/DataViews.py index b18a813..0a4569f 100644 --- a/src/silx/gui/data/DataViews.py +++ b/src/silx/gui/data/DataViews.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2020 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -876,7 +875,9 @@ class _Plot1dView(DataView): def createWidget(self, parent): from silx.gui import plot - return plot.Plot1D(parent=parent) + widget = plot.Plot1D(parent=parent) + widget.setGraphGrid(True) + return widget def clear(self): self.getWidget().clear() @@ -1759,6 +1760,8 @@ class _NXdataImageView(_NXdataBaseDataView): y_axis, x_axis = nxd.axes[img_slicing] y_label, x_label = nxd.axes_names[img_slicing] y_scale, x_scale = nxd.plot_style.axes_scale_types[img_slicing] + x_units = get_attr_as_unicode(x_axis, 'units') if x_axis else None + y_units = get_attr_as_unicode(y_axis, 'units') if y_axis else None self.getWidget().setImageData( [nxd.signal] + nxd.auxiliary_signals, @@ -1766,7 +1769,9 @@ class _NXdataImageView(_NXdataBaseDataView): signals_names=[nxd.signal_name] + nxd.auxiliary_signals_names, xlabel=x_label, ylabel=y_label, title=nxd.title, isRgba=isRgba, - xscale=x_scale, yscale=y_scale) + xscale=x_scale, yscale=y_scale, + keep_ratio=(x_units == y_units), + ) def getDataPriority(self, data, info): data = self.normalizeData(data) @@ -1804,13 +1809,17 @@ class _NXdataComplexImageView(_NXdataBaseDataView): img_slicing = slice(-2, None) y_axis, x_axis = nxd.axes[img_slicing] y_label, x_label = nxd.axes_names[img_slicing] + x_units = get_attr_as_unicode(x_axis, 'units') if x_axis else None + y_units = get_attr_as_unicode(y_axis, 'units') if y_axis else None self.getWidget().setImageData( [nxd.signal] + nxd.auxiliary_signals, x_axis=x_axis, y_axis=y_axis, signals_names=[nxd.signal_name] + nxd.auxiliary_signals_names, xlabel=x_label, ylabel=y_label, - title=nxd.title) + title=nxd.title, + keep_ratio=(x_units == y_units), + ) def axesNames(self, data, info): # disabled (used by default axis selector widget in Hdf5Viewer) diff --git a/src/silx/gui/data/Hdf5TableView.py b/src/silx/gui/data/Hdf5TableView.py index 9d65a84..f3fbb69 100644 --- a/src/silx/gui/data/Hdf5TableView.py +++ b/src/silx/gui/data/Hdf5TableView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -26,7 +25,6 @@ This module define model and widget to display 1D slices from numpy array using compound data types or hdf5 databases. """ -from __future__ import division __authors__ = ["V. Valls"] __license__ = "MIT" @@ -450,8 +448,9 @@ class Hdf5TableModel(HierarchicalTableView.HierarchicalTableModel): return firstExtSource if firstExtSource[0] == ".": - firstExtSource.pop(0) - return os.path.join(os.path.dirname(filename), firstExtSource) + return filename + firstExtSource[1:] + else: + return os.path.join(os.path.dirname(filename), firstExtSource) self.__data.addHeaderRow(headerLabel="External sources") self.__data.addHeaderValueRow("Type", extType) diff --git a/src/silx/gui/data/HexaTableView.py b/src/silx/gui/data/HexaTableView.py index 9e00a7b..30f62f0 100644 --- a/src/silx/gui/data/HexaTableView.py +++ b/src/silx/gui/data/HexaTableView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -26,7 +25,6 @@ This module defines model and widget to display raw data using an hexadecimal viewer. """ -from __future__ import division import collections diff --git a/src/silx/gui/data/NXdataWidgets.py b/src/silx/gui/data/NXdataWidgets.py index 54ea287..b9e34d2 100644 --- a/src/silx/gui/data/NXdataWidgets.py +++ b/src/silx/gui/data/NXdataWidgets.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -76,6 +75,7 @@ class ArrayCurvePlot(qt.QWidget): self.__values = None self._plot = Plot1D(self) + self._plot.setGraphGrid(True) self._selector = NumpyAxesSelector(self) self._selector.setNamedAxesSelectorVisibility(False) @@ -412,7 +412,8 @@ class ArrayImagePlot(qt.QWidget): signals_names=None, xlabel=None, ylabel=None, title=None, isRgba=False, - xscale=None, yscale=None): + xscale=None, yscale=None, + keep_ratio: bool=True): """ :param signals: list of n-D datasets, whose last 2 dimensions are used as the @@ -430,6 +431,7 @@ class ArrayImagePlot(qt.QWidget): :param isRgba: True if data is a 3D RGBA image :param str xscale: Scale of X axis in (None, 'linear', 'log') :param str yscale: Scale of Y axis in (None, 'linear', 'log') + :param keep_ratio: Toggle plot keep aspect ratio """ self._selector.selectionChanged.disconnect(self._updateImage) self._auxSigSlider.valueChanged.disconnect(self._sliderIdxChanged) @@ -465,6 +467,7 @@ class ArrayImagePlot(qt.QWidget): self._axis_scales = xscale, yscale self._updateImage() + self._plot.setKeepDataAspectRatio(keep_ratio) self._plot.resetZoom() self._selector.selectionChanged.connect(self._updateImage) @@ -629,7 +632,8 @@ class ArrayComplexImagePlot(qt.QWidget): x_axis=None, y_axis=None, signals_names=None, xlabel=None, ylabel=None, - title=None): + title=None, + keep_ratio: bool=True): """ :param signals: list of n-D datasets, whose last 2 dimensions are used as the @@ -644,6 +648,7 @@ class ArrayComplexImagePlot(qt.QWidget): :param xlabel: Label for X axis :param ylabel: Label for Y axis :param title: Graph title + :param keep_ratio: Toggle plot keep aspect ratio """ self._selector.selectionChanged.disconnect(self._updateImage) self._auxSigSlider.valueChanged.disconnect(self._sliderIdxChanged) @@ -673,6 +678,7 @@ class ArrayComplexImagePlot(qt.QWidget): self._auxSigSlider.setValue(0) self._updateImage() + self._plot.setKeepDataAspectRatio(keep_ratio) self._plot.getPlot().resetZoom() self._selector.selectionChanged.connect(self._updateImage) diff --git a/src/silx/gui/data/NumpyAxesSelector.py b/src/silx/gui/data/NumpyAxesSelector.py index e6da0d4..50b8dcd 100644 --- a/src/silx/gui/data/NumpyAxesSelector.py +++ b/src/silx/gui/data/NumpyAxesSelector.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility @@ -25,7 +24,6 @@ """This module defines a widget able to convert a numpy array from n-dimensions to a numpy array with less dimensions. """ -from __future__ import division __authors__ = ["V. Valls"] __license__ = "MIT" diff --git a/src/silx/gui/data/RecordTableView.py b/src/silx/gui/data/RecordTableView.py index ea73c62..9079ba6 100644 --- a/src/silx/gui/data/RecordTableView.py +++ b/src/silx/gui/data/RecordTableView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -26,7 +25,6 @@ This module define model and widget to display 1D slices from numpy array using compound data types or hdf5 databases. """ -from __future__ import division import itertools import numpy diff --git a/src/silx/gui/data/TextFormatter.py b/src/silx/gui/data/TextFormatter.py index b6baca4..d409381 100644 --- a/src/silx/gui/data/TextFormatter.py +++ b/src/silx/gui/data/TextFormatter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/data/_VolumeWindow.py b/src/silx/gui/data/_VolumeWindow.py index 03b6876..fa2730c 100644 --- a/src/silx/gui/data/_VolumeWindow.py +++ b/src/silx/gui/data/_VolumeWindow.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/data/__init__.py b/src/silx/gui/data/__init__.py index 560062d..59d32f1 100644 --- a/src/silx/gui/data/__init__.py +++ b/src/silx/gui/data/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/data/setup.py b/src/silx/gui/data/setup.py deleted file mode 100644 index 23ccbdd..0000000 --- a/src/silx/gui/data/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["P. Knobel"] -__license__ = "MIT" -__date__ = "16/01/2017" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('data', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/gui/data/test/__init__.py b/src/silx/gui/data/test/__init__.py index 7790ee5..1d8207b 100644 --- a/src/silx/gui/data/test/__init__.py +++ b/src/silx/gui/data/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/data/test/test_arraywidget.py b/src/silx/gui/data/test/test_arraywidget.py index c84a34f..024383d 100644 --- a/src/silx/gui/data/test/test_arraywidget.py +++ b/src/silx/gui/data/test/test_arraywidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/data/test/test_dataviewer.py b/src/silx/gui/data/test/test_dataviewer.py index 30b76ce..80f47b7 100644 --- a/src/silx/gui/data/test/test_dataviewer.py +++ b/src/silx/gui/data/test/test_dataviewer.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2020 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -192,18 +191,36 @@ class _TestAbstractDataViewer(TestCaseQt): listener.clear() def test_change_display_mode(self): + listener = SignalListener() data = numpy.arange(10 ** 4) data.shape = [10] * 4 widget = self.create_widget() + widget.selectionChanged.connect(listener) widget.setData(data) + widget.setDisplayMode(DataViews.PLOT1D_MODE) self.assertEqual(widget.displayedView().modeId(), DataViews.PLOT1D_MODE) + self.qWait(200) + assert listener.arguments() == [((0, 0, 0, slice(None)), None)] + listener.clear() + widget.setDisplayMode(DataViews.IMAGE_MODE) self.assertEqual(widget.displayedView().modeId(), DataViews.IMAGE_MODE) + self.qWait(200) + assert listener.arguments() == [((0, 0, slice(None), slice(None)), None)] + listener.clear() + widget.setDisplayMode(DataViews.RAW_MODE) self.assertEqual(widget.displayedView().modeId(), DataViews.RAW_MODE) + self.qWait(200) + # Changing from 2D to 2D view: Selection didn't changed + assert listener.callCount() == 0 + widget.setDisplayMode(DataViews.EMPTY_MODE) self.assertEqual(widget.displayedView().modeId(), DataViews.EMPTY_MODE) + self.qWait(200) + assert listener.arguments() == [(None, None)] + listener.clear() def test_create_default_views(self): widget = self.create_widget() diff --git a/src/silx/gui/data/test/test_numpyaxesselector.py b/src/silx/gui/data/test/test_numpyaxesselector.py index 37b8d3e..4a53149 100644 --- a/src/silx/gui/data/test/test_numpyaxesselector.py +++ b/src/silx/gui/data/test/test_numpyaxesselector.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/data/test/test_textformatter.py b/src/silx/gui/data/test/test_textformatter.py index af41def..b82cc7a 100644 --- a/src/silx/gui/data/test/test_textformatter.py +++ b/src/silx/gui/data/test/test_textformatter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/AbstractDataFileDialog.py b/src/silx/gui/dialog/AbstractDataFileDialog.py index 5272f48..f656bb2 100644 --- a/src/silx/gui/dialog/AbstractDataFileDialog.py +++ b/src/silx/gui/dialog/AbstractDataFileDialog.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -35,7 +34,6 @@ import sys import os import logging import functools -from distutils.version import LooseVersion import numpy @@ -149,13 +147,13 @@ class _SideBar(qt.QListView): :rtype: List[str] """ urls = [] - version = LooseVersion(qt.qVersion()) + version = tuple(map(int, qt.qVersion().split('.')[:3])) feed_sidebar = True if not DEFAULT_SIDEBAR_URL: _logger.debug("Skip default sidebar URLs (from setted variable)") feed_sidebar = False - elif version < LooseVersion("5.11.2") and qt.BINDING == "PyQt5" and sys.platform in ["linux", "linux2"]: + elif version < (5, 11, 2) and qt.BINDING == "PyQt5" and sys.platform in ["linux", "linux2"]: # Avoid segfault on PyQt5 + gtk _logger.debug("Skip default sidebar URLs (avoid PyQt5 segfault)") feed_sidebar = False @@ -429,7 +427,7 @@ class _Browser(qt.QStackedWidget): self.__detailView.header().restoreState(headerData) viewMode = stream.readInt32() - self.setViewMode(viewMode) + self.setViewMode(qt.QFileDialog.ViewMode(viewMode)) return True def saveState(self): @@ -444,7 +442,10 @@ class _Browser(qt.QStackedWidget): stream.writeQString(nameId) stream.writeInt32(self.__serialVersion) stream.writeQVariant(self.__detailView.header().saveState()) - stream.writeInt32(self.viewMode()) + viewMode = self.viewMode() + if qt.BINDING == 'PyQt6': # No auto conversion to int + viewMode = viewMode.value + stream.writeInt32(viewMode) return data @@ -1695,7 +1696,7 @@ class AbstractDataFileDialog(qt.QDialog): if workingDirectory is not None: self.setDirectory(workingDirectory) result &= self.__browser.restoreState(browserData) - self.setViewMode(viewMode) + self.setViewMode(qt.QFileDialog.ViewMode(viewMode)) colormap = self.colormap() if colormap is not None: result &= self.colormap().restoreState(colormapData) @@ -1721,7 +1722,10 @@ class AbstractDataFileDialog(qt.QDialog): stream.writeQStringList(strings) stream.writeQString(u"%s" % self.directory()) stream.writeQVariant(self.__browser.saveState()) - stream.writeInt32(self.viewMode()) + viewMode = self.viewMode() + if qt.BINDING == 'PyQt6': # No auto conversion to int + viewMode = viewMode.value + stream.writeInt32(viewMode) colormap = self.colormap() if colormap is not None: stream.writeQVariant(self.colormap().saveState()) diff --git a/src/silx/gui/dialog/ColormapDialog.py b/src/silx/gui/dialog/ColormapDialog.py index 2506e2a..f3f38b5 100644 --- a/src/silx/gui/dialog/ColormapDialog.py +++ b/src/silx/gui/dialog/ColormapDialog.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -81,7 +80,7 @@ from silx.gui.plot import items from silx.gui import icons from silx.gui.qt import inspect as qtinspect from silx.gui.widgets.ColormapNameComboBox import ColormapNameComboBox -from silx.gui.widgets.WaitingPushButton import WaitingPushButton +from silx.gui.widgets.FormGridLayout import FormGridLayout from silx.math.histogram import Histogramnd from silx.utils import deprecation from silx.gui.plot.items.roi import RectangleROI @@ -128,6 +127,20 @@ class _BoundaryWidget(qt.QWidget): self.setLayout(qt.QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self._numVal = FloatEdit(parent=self, value=value) + + self._iconAuto = icons.getQIcon('scale-auto') + self._iconFixed = icons.getQIcon('scale-fixed') + + self._autoToggleAction = qt.QAction(self) + self._autoToggleAction.setText("Auto scale") + self._autoToggleAction.setToolTip("Toggle auto scale") + self._autoToggleAction.setCheckable(True) + self._autoToggleAction.setIcon(self._iconFixed) + self._autoToggleAction.setChecked(False) + self._autoToggleAction.toggled.connect(self._autoToggled) + + self._numVal.addAction(self._autoToggleAction, qt.QLineEdit.LeadingPosition) + self.layout().addWidget(self._numVal) self._autoCB = qt.QCheckBox('auto', parent=self) self.layout().addWidget(self._autoCB) @@ -174,7 +187,7 @@ class _BoundaryWidget(qt.QWidget): return self._numVal.value() def _autoToggled(self, enabled): - self._numVal.setEnabled(not enabled) + self._updateAutoScaleState(enabled) self._updateDisplayedText() self.sigAutoScaleChanged.emit(enabled) @@ -198,14 +211,27 @@ class _BoundaryWidget(qt.QWidget): if not self.__textWasEdited: self._numVal.setValue(value) self.__realValue = value - self._numVal.setEnabled(not isAuto) + self._updateAutoScaleState(isAuto) + + def _updateAutoScaleState(self, isAutoScale): + self._numVal.setReadOnly(isAutoScale) + palette = qt.QPalette() + if isAutoScale: + color = palette.color(qt.QPalette.Disabled, qt.QPalette.Base) + icon = self._iconAuto + else: + color = palette.color(qt.QPalette.Normal, qt.QPalette.Base) + icon = self._iconFixed + palette.setColor(qt.QPalette.Base, color) + self._numVal.setPalette(palette) + self._autoToggleAction.setIcon(icon) class _AutoscaleModeComboBox(qt.QComboBox): DATA = { Colormap.MINMAX: ("Min/max", "Use the data min/max"), - Colormap.STDDEV3: ("Mean ± 3 × stddev", "Use the data mean ± 3 × standard deviation"), + Colormap.STDDEV3: ("Mean±3std", "Use the data mean ± 3 × standard deviation"), } def __init__(self, parent: qt.QWidget): @@ -248,80 +274,31 @@ class _AutoscaleModeComboBox(qt.QComboBox): self.setCurrentIndex(self.count() - 1) -class _AutoScaleButtons(qt.QWidget): +class _AutoScaleButton(qt.QPushButton): autoRangeChanged = qt.Signal(object) def __init__(self, parent=None): - qt.QWidget.__init__(self, parent=parent) - layout = qt.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - - self.setFocusPolicy(qt.Qt.NoFocus) - - self._bothAuto = qt.QPushButton(self) - self._bothAuto.setText("Autoscale") - self._bothAuto.setToolTip("Enable/disable the autoscale for both min and max") - self._bothAuto.setCheckable(True) - self._bothAuto.toggled[bool].connect(self.__bothToggled) - self._bothAuto.setFocusPolicy(qt.Qt.TabFocus) - - self._minAuto = qt.QCheckBox(self) - self._minAuto.setText("") - self._minAuto.setToolTip("Enable/disable the autoscale for min") - self._minAuto.toggled[bool].connect(self.__minToggled) - self._minAuto.setFocusPolicy(qt.Qt.TabFocus) - - self._maxAuto = qt.QCheckBox(self) - self._maxAuto.setText("") - self._maxAuto.setToolTip("Enable/disable the autoscale for max") - self._maxAuto.toggled[bool].connect(self.__maxToggled) - self._maxAuto.setFocusPolicy(qt.Qt.TabFocus) - - layout.addStretch(1) - layout.addWidget(self._minAuto) - layout.addSpacing(20) - layout.addWidget(self._bothAuto) - layout.addSpacing(20) - layout.addWidget(self._maxAuto) - layout.addStretch(1) - - def __bothToggled(self, checked): + qt.QPushButton.__init__(self, parent=parent) + self.setText("Autoscale") + self.setToolTip("Enable/disable the autoscale for both min and max") + self.setCheckable(True) + self.toggled[bool].connect(self.__toggled) + self.setFocusPolicy(qt.Qt.TabFocus) + + def __toggled(self, checked): autoRange = checked, checked self.setAutoRange(autoRange) self.autoRangeChanged.emit(autoRange) - def __minToggled(self, checked): - autoRange = self.getAutoRange() - self.setAutoRange(autoRange) - self.autoRangeChanged.emit(autoRange) - - def __maxToggled(self, checked): - autoRange = self.getAutoRange() - self.setAutoRange(autoRange) - self.autoRangeChanged.emit(autoRange) - def setAutoRangeFromColormap(self, colormap): vRange = colormap.getVRange() autoRange = vRange[0] is None, vRange[1] is None self.setAutoRange(autoRange) def setAutoRange(self, autoRange): - if autoRange[0] == autoRange[1]: - with utils.blockSignals(self._bothAuto): - self._bothAuto.setChecked(autoRange[0]) - else: - with utils.blockSignals(self._bothAuto): - self._bothAuto.setChecked(False) - with utils.blockSignals(self._minAuto): - self._minAuto.setChecked(autoRange[0]) - with utils.blockSignals(self._maxAuto): - self._maxAuto.setChecked(autoRange[1]) - - def getAutoRange(self): - return self._minAuto.isChecked(), self._maxAuto.isChecked() - + with utils.blockSignals(self): + self.setChecked(autoRange[0] if autoRange[0] == autoRange[1] else False) @enum.unique class _DataInPlotMode(enum.Enum): @@ -333,7 +310,7 @@ class _DataInPlotMode(enum.Enum): class _ColormapHistogram(qt.QWidget): """Display the colormap and the data as a plot.""" - sigRangeMoving = qt.Signal(object, object) + sigRangeMoving = qt.Signal(object, object, object) """Emitted when a mouse interaction moves the location of the colormap range in the plot. @@ -341,15 +318,17 @@ class _ColormapHistogram(qt.QWidget): - vmin: A float value if this range was moved, else None - vmax: A float value if this range was moved, else None + - gammaPos: A float value if this range was moved, else None """ - sigRangeMoved = qt.Signal(object, object) + sigRangeMoved = qt.Signal(object, object, object) """Emitted when a mouse interaction stop. This signal contains 2 elements: - vmin: A float value if this range was moved, else None - vmax: A float value if this range was moved, else None + - gammaPos: A float value if this range was moved, else None """ def __init__(self, parent): @@ -361,7 +340,7 @@ class _ColormapHistogram(qt.QWidget): self._histogramData = {} """Histogram displayed in the plot""" - self._dragging = False, False + self._dragging = False, False, False """True, if the min or the max handle is dragging""" self._dataRange = {} @@ -528,7 +507,8 @@ class _ColormapHistogram(qt.QWidget): def _initPlot(self): """Init the plot to display the range and the values""" self._plot = PlotWidget(self) - self._plot.setDataMargins(0.125, 0.125, 0.125, 0.125) + self._plot.setAxesDisplayed(False) + self._plot.setDataMargins(0.125, 0.125, 0.01, 0.01) self._plot.getXAxis().setLabel("Data Values") self._plot.getYAxis().setLabel("") self._plot.setInteractiveMode('select', zoomOnWheel=False) @@ -600,20 +580,26 @@ class _ColormapHistogram(qt.QWidget): if kind == 'markerMoving': value = event['xdata'] if event['label'] == 'Min': - self._dragging = True, False + self._dragging = True, False, False self._finiteRange = value, self._finiteRange[1] - self._last = value, None + self._last = value, None, None + self._updateGammaPosition() self.sigRangeMoving.emit(*self._last) elif event['label'] == 'Max': - self._dragging = False, True + self._dragging = False, True, False self._finiteRange = self._finiteRange[0], value - self._last = None, value + self._last = None, value, None + self._updateGammaPosition() + self.sigRangeMoving.emit(*self._last) + elif event['label'] == 'Gamma': + self._dragging = False, False, True + self._last = None, None, value self.sigRangeMoving.emit(*self._last) self._updateLutItem(self._finiteRange) elif kind == 'markerMoved': self.sigRangeMoved.emit(*self._last) self._plot.resetZoom() - self._dragging = False, False + self._dragging = False, False, False else: pass @@ -635,11 +621,12 @@ class _ColormapHistogram(qt.QWidget): draggable=isDraggable, color="blue", constraint=self._plotMinMarkerConstraint) + self._updateGammaPosition() if posMax is not None and not self._dragging[1]: self._plot.addXMarker( posMax, legend='Max', - text='Max', + text='\n\nMax', draggable=isDraggable, color="blue", constraint=self._plotMaxMarkerConstraint) @@ -647,6 +634,42 @@ class _ColormapHistogram(qt.QWidget): self._updateLutItem((posMin, posMax)) self._plot.resetZoom() + def _updateGammaPosition(self): + colormap = self.getColormap() + posMin, posMax = self._getDisplayableRange() + + if colormap is None: + gamma = None + else: + if colormap.getNormalization() == Colormap.GAMMA: + gamma = colormap.getGammaNormalizationParameter() + else: + gamma = None + + if gamma is not None: + if not self._dragging[2]: + posRange = posMax - posMin + if posRange > 0: + gammaPos = posMin + posRange * 0.5**(1/gamma) + else: + gammaPos = posMin + marker = self._plot._getMarker( + self._plot.addXMarker( + gammaPos, + legend='Gamma', + text='\nGamma', + draggable=True, + color="blue", + constraint=self._plotGammaMarkerConstraint, + ) + ) + marker.setZValue(2) + else: + try: + self._plot.removeMarker('Gamma') + except Exception: + pass + def _updateLutItem(self, vRange): colormap = self.getColormap() if colormap is None: @@ -718,6 +741,15 @@ class _ColormapHistogram(qt.QWidget): return x, y return max(x, vmin), y + def _plotGammaMarkerConstraint(self, x, y): + """Constraint of the gamma marker""" + vmin, vmax = self.getFiniteRange() + if vmin is not None: + x = max(x, vmin) + if vmax is not None: + x = min(x, vmax) + return x, y + def _setDataInPlotMode(self, mode): if self._dataInPlotMode == mode: return @@ -829,6 +861,9 @@ class ColormapDialog(qt.QDialog): self._item = None """Weak ref to an external item""" + self._colormapped = None + """Weak ref to reduce data update""" + self._colormapChange = utils.LockReentrant() """Used as a semaphore to avoid editing the colormap object when we are only attempt to display it. @@ -873,7 +908,7 @@ class ColormapDialog(qt.QDialog): self._gammaSpinBox = qt.QDoubleSpinBox(parent=self) self._gammaSpinBox.setEnabled(False) - self._gammaSpinBox.setRange(0., 1000.) + self._gammaSpinBox.setRange(0.01, 100.) self._gammaSpinBox.setDecimals(4) if hasattr(qt.QDoubleSpinBox, "setStepType"): # Introduced in Qt 5.12 @@ -891,13 +926,15 @@ class ColormapDialog(qt.QDialog): self._minValue = _BoundaryWidget(parent=self, value=1.0) self._minValue.sigAutoScaleChanged.connect(self._minAutoscaleUpdated) self._minValue.sigValueChanged.connect(self._minValueUpdated) + self._minValue.setMinimumWidth(140) # Max row self._maxValue = _BoundaryWidget(parent=self, value=10.0) self._maxValue.sigAutoScaleChanged.connect(self._maxAutoscaleUpdated) self._maxValue.sigValueChanged.connect(self._maxValueUpdated) + self._maxValue.setMinimumWidth(140) - self._autoButtons = _AutoScaleButtons(self) + self._autoButtons = _AutoScaleButton(self) self._autoButtons.autoRangeChanged.connect(self._autoRangeButtonsUpdated) rangeLayout = qt.QGridLayout() @@ -909,15 +946,20 @@ class ColormapDialog(qt.QDialog): labelMax = qt.QLabel("Max", self) labelMax.setAlignment(qt.Qt.AlignHCenter) labelMax.setFont(miniFont) - rangeLayout.addWidget(labelMin, 0, 0) - rangeLayout.addWidget(labelMax, 0, 1) - rangeLayout.addWidget(self._minValue, 1, 0) - rangeLayout.addWidget(self._maxValue, 1, 1) - rangeLayout.addWidget(self._autoButtons, 2, 0, 1, -1, qt.Qt.AlignCenter) + rangeLayout.addWidget(labelMin, 0, 1) + rangeLayout.addWidget(labelMax, 0, 3) + rangeLayout.addWidget(self._minValue, 1, 1) + rangeLayout.addWidget(self._maxValue, 1, 3) + rangeLayout.setColumnStretch(0, 1) + rangeLayout.setColumnStretch(1, 2) + rangeLayout.setColumnStretch(2, 1) + rangeLayout.setColumnStretch(3, 2) + rangeLayout.setColumnStretch(4, 1) self._histoWidget = _ColormapHistogram(self) self._histoWidget.sigRangeMoving.connect(self._histogramRangeMoving) self._histoWidget.sigRangeMoved.connect(self._histogramRangeMoved) + self._histoWidget.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding) # Scale to buttons self._visibleAreaButton = qt.QPushButton(self) @@ -930,12 +972,12 @@ class ColormapDialog(qt.QDialog): # Place-holder for selected area ROI manager self._roiForColormapManager = None - self._selectedAreaButton = WaitingPushButton(self) + self._selectedAreaButton = qt.QPushButton(self) + self._selectedAreaButton.setCheckable(True) self._selectedAreaButton.setEnabled(False) self._selectedAreaButton.setText("Selection") self._selectedAreaButton.setIcon(icons.getQIcon("add-shape-rectangle")) self._selectedAreaButton.setCheckable(True) - self._selectedAreaButton.setDisabledWhenWaiting(False) self._selectedAreaButton.toggled.connect( self._handleScaleToSelectionToggled, type=qt.Qt.QueuedConnection) @@ -966,31 +1008,38 @@ class ColormapDialog(qt.QDialog): self.setModal(self.isModal()) - formLayout = qt.QFormLayout(self) - formLayout.setContentsMargins(10, 10, 10, 10) - formLayout.addRow('Colormap:', self._comboBoxColormap) - formLayout.addRow('Normalization:', self._comboBoxNormalization) - formLayout.addRow('Gamma:', self._gammaSpinBox) - formLayout.addRow(self._histoWidget) - formLayout.addRow(rangeLayout) - label = qt.QLabel('Mode:', self) - self._autoscaleModeLabel = label - label.setToolTip("Mode for autoscale. Algorithm used to find range in auto scale.") - formLayout.addItem(qt.QSpacerItem(1, 1, qt.QSizePolicy.Fixed, qt.QSizePolicy.Fixed)) - formLayout.addRow(label, autoScaleCombo) - layout = qt.QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._visibleAreaButton) layout.addWidget(self._selectedAreaButton) - self._scaleToAreaGroup = qt.QGroupBox('Scale to:', self) + layout.addStretch() + self._scaleToAreaGroup = qt.QWidget(self) self._scaleToAreaGroup.setLayout(layout) self._scaleToAreaGroup.setVisible(False) - formLayout.addRow(self._scaleToAreaGroup) + layoutScale = qt.QHBoxLayout() + layoutScale.setContentsMargins(0, 0, 0, 0) + layoutScale.addWidget(self._autoButtons) + layoutScale.addWidget(self._autoScaleCombo) + layoutScale.addStretch() + + + formLayout = FormGridLayout(self) + formLayout.setContentsMargins(10, 10, 10, 10) + + formLayout.addRow('Colormap:', self._comboBoxColormap) + formLayout.addRow('Normalization:', self._comboBoxNormalization) + formLayout.addRow('Gamma:', self._gammaSpinBox) + + formLayout.addItem(qt.QSpacerItem(1, 1, qt.QSizePolicy.Fixed, qt.QSizePolicy.Fixed)) + formLayout.addRow(self._histoWidget) + formLayout.addRow(rangeLayout) + formLayout.addItem(qt.QSpacerItem(1, 1, qt.QSizePolicy.Fixed, qt.QSizePolicy.Fixed)) + formLayout.addRow('Scale:', layoutScale) + formLayout.addRow("Fixed scale on:", self._scaleToAreaGroup) formLayout.addRow(self._buttonsModal) formLayout.addRow(self._buttonsNonModal) - formLayout.setSizeConstraint(qt.QLayout.SetMinimumSize) + formLayout.setSizeConstraint(qt.QLayout.SetMinAndMaxSize) self.setTabOrder(self._comboBoxColormap, self._comboBoxNormalization) self.setTabOrder(self._comboBoxNormalization, self._gammaSpinBox) @@ -1003,7 +1052,6 @@ class ColormapDialog(qt.QDialog): self.setTabOrder(self._selectedAreaButton, self._buttonsModal) self.setTabOrder(self._buttonsModal, self._buttonsNonModal) - self.setFixedSize(self.sizeHint()) self._applyColormap() def _invalidateColormap(self): @@ -1041,10 +1089,14 @@ class ColormapDialog(qt.QDialog): super(ColormapDialog, self).closeEvent(event) def hideEvent(self, event): + if self._selectedAreaButton.isChecked(): + self._selectedAreaButton.setChecked(False) self.visibleChanged.emit(False) super(ColormapDialog, self).hideEvent(event) def close(self): + if self._selectedAreaButton.isChecked(): + self._selectedAreaButton.setChecked(False) self.accept() qt.QDialog.close(self) @@ -1196,10 +1248,21 @@ class ColormapDialog(qt.QDialog): the data range or the histogram of the data using :meth:`setDataRange` and :meth:`setHistogram` """ - # While event from items are not supported, we can't ignore dup items - # old = self._getItem() - # if old is item: - # return + old = self._getItem() + if old is item: + # While event from items are not supported, we can't ignore dup items + if item is not None: + array = item.getColormappedData(copy=False) + else: + array = None + colormapped = self._colormapped + if colormapped is not None: + oldArray = colormapped() + else: + oldArray = None + if oldArray is array: + return + self._data = None self._itemHolder = None try: @@ -1252,7 +1315,12 @@ class ColormapDialog(qt.QDialog): return data item = self._getItem() if item is not None: - return item.getColormappedData(copy=False) + colormapped = item.getColormappedData(copy=False) + if colormapped is not None: + self._colormapped = weakref.ref(colormapped) + else: + self._colormapped = None + return colormapped return None def _colormapAboutToFinalize(self, weakrefColormap): @@ -1418,7 +1486,6 @@ class ColormapDialog(qt.QDialog): self._histoWidget.setFiniteRange((xmin, xmax)) with utils.blockSignals(self._autoButtons): self._autoButtons.setAutoRange((autoMin, autoMax)) - self._autoscaleModeLabel.setEnabled(autoMin or autoMax) def accept(self): self.storeCurrentState() @@ -1487,7 +1554,6 @@ class ColormapDialog(qt.QDialog): self._minValue.setEnabled(False) self._maxValue.setEnabled(False) self._autoButtons.setEnabled(False) - self._autoscaleModeLabel.setEnabled(False) self._histoWidget.setVisible(False) self._histoWidget.setFiniteRange((None, None)) else: @@ -1508,7 +1574,7 @@ class ColormapDialog(qt.QDialog): self._gammaSpinBox.setValue( colormap.getGammaNormalizationParameter()) self._gammaSpinBox.setEnabled( - colormap.getNormalization() == 'gamma' and + colormap.getNormalization() == Colormap.GAMMA and colormap.isEditable()) with utils.blockSignals(self._autoScaleCombo): self._autoScaleCombo.setCurrentMode(colormap.getAutoscaleMode()) @@ -1530,7 +1596,6 @@ class ColormapDialog(qt.QDialog): with utils.blockSignals(self._maxValue): self._maxValue.setValue(vmax or dataRange[1], isAuto=vmax is None) self._maxValue.setEnabled(colormap.isEditable()) - self._autoscaleModeLabel.setEnabled(vmin is None or vmax is None) with utils.blockSignals(self._histoWidget): self._histoWidget.setVisible(True) @@ -1653,12 +1718,13 @@ class ColormapDialog(qt.QDialog): self._maxValue.setValue(xmax) self._setColormapRange(xmin, xmax) - def _histogramRangeMoving(self, vmin, vmax): + def _histogramRangeMoving(self, vmin, vmax, gammaPos): """Callback executed when for colormap range displayed in the histogram widget is moving. :param vmin: Update of the minimum range, else None :param vmax: Update of the maximum range, else None + :param gammaPos: Update of the gamma location, else None """ colormap = self.getColormap() if vmin is not None: @@ -1669,11 +1735,31 @@ class ColormapDialog(qt.QDialog): with self._colormapChange: colormap.setVMax(vmax) self._maxValue.setValue(vmax) + if gammaPos is not None: + vmin, vmax = self._histoWidget.getFiniteRange() + if vmax < vmin: + gamma = 1 + elif gammaPos >= vmax: + gamma = self._gammaSpinBox.maximum() + elif gammaPos <= vmin: + gamma = self._gammaSpinBox.minimum() + else: + gamma = numpy.clip( + numpy.log(0.5)/numpy.log((gammaPos - vmin) / (vmax - vmin)), + self._gammaSpinBox.minimum(), + self._gammaSpinBox.maximum(), + ) + with self._colormapChange: + colormap.setGammaNormalizationParameter(gamma) + with utils.blockSignals(self._gammaSpinBox): + self._gammaSpinBox.setValue(gamma) - def _histogramRangeMoved(self, vmin, vmax): + def _histogramRangeMoved(self, vmin, vmax, gammaPos): """Callback executed when for colormap range displayed in the histogram widget has finished to move """ + if vmin is None and vmax is None: + return xmin = self._minValue.getValue() xmax = self._maxValue.getValue() if vmin is None: @@ -1713,7 +1799,6 @@ class ColormapDialog(qt.QDialog): self._roiForColormapManager = None if not checked: # Reset button status - self._selectedAreaButton.setWaiting(False) self._selectedAreaButton.setText("Selection") return @@ -1727,7 +1812,6 @@ class ColormapDialog(qt.QDialog): self._selectedAreaButton.setChecked(False) return # no-op - self._selectedAreaButton.setWaiting(True) self._selectedAreaButton.setText("Draw Area...") self._roiForColormapManager = RegionOfInterestManager(parent=plotWidget) @@ -1743,11 +1827,12 @@ class ColormapDialog(qt.QDialog): self._selectedAreaButton.setChecked(False) def __roiFinalized(self, roi): - self._selectedAreaButton.setChecked(False) if roi is not None: ox, oy = roi.getOrigin() width, height = roi.getSize() self.setColormapRangeFromDataBounds((ox, ox+width, oy, oy+height)) + # clear ROI + self._roiForColormapManager.removeRoi(roi) def keyPressEvent(self, event): """Override key handling. diff --git a/src/silx/gui/dialog/DataFileDialog.py b/src/silx/gui/dialog/DataFileDialog.py index 0d0382d..75b1721 100644 --- a/src/silx/gui/dialog/DataFileDialog.py +++ b/src/silx/gui/dialog/DataFileDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/DatasetDialog.py b/src/silx/gui/dialog/DatasetDialog.py index c5ee295..5d8af0d 100644 --- a/src/silx/gui/dialog/DatasetDialog.py +++ b/src/silx/gui/dialog/DatasetDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/FileTypeComboBox.py b/src/silx/gui/dialog/FileTypeComboBox.py index 92529bc..0ffc3a5 100644 --- a/src/silx/gui/dialog/FileTypeComboBox.py +++ b/src/silx/gui/dialog/FileTypeComboBox.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/GroupDialog.py b/src/silx/gui/dialog/GroupDialog.py index e129a51..fb85d83 100644 --- a/src/silx/gui/dialog/GroupDialog.py +++ b/src/silx/gui/dialog/GroupDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/ImageFileDialog.py b/src/silx/gui/dialog/ImageFileDialog.py index 83c6d95..ed455f3 100644 --- a/src/silx/gui/dialog/ImageFileDialog.py +++ b/src/silx/gui/dialog/ImageFileDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/SafeFileIconProvider.py b/src/silx/gui/dialog/SafeFileIconProvider.py index 1e06b64..141bedf 100644 --- a/src/silx/gui/dialog/SafeFileIconProvider.py +++ b/src/silx/gui/dialog/SafeFileIconProvider.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/SafeFileSystemModel.py b/src/silx/gui/dialog/SafeFileSystemModel.py index 1ec7153..b9f3913 100644 --- a/src/silx/gui/dialog/SafeFileSystemModel.py +++ b/src/silx/gui/dialog/SafeFileSystemModel.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/__init__.py b/src/silx/gui/dialog/__init__.py index 77c5949..c1dc89a 100644 --- a/src/silx/gui/dialog/__init__.py +++ b/src/silx/gui/dialog/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/setup.py b/src/silx/gui/dialog/setup.py deleted file mode 100644 index 48ab8d8..0000000 --- a/src/silx/gui/dialog/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "23/10/2017" - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('dialog', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/gui/dialog/test/__init__.py b/src/silx/gui/dialog/test/__init__.py index 71128fb..b03339f 100644 --- a/src/silx/gui/dialog/test/__init__.py +++ b/src/silx/gui/dialog/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/dialog/test/test_colormapdialog.py b/src/silx/gui/dialog/test/test_colormapdialog.py index 16a5ab2..1bfd584 100644 --- a/src/silx/gui/dialog/test/test_colormapdialog.py +++ b/src/silx/gui/dialog/test/test_colormapdialog.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -51,7 +50,7 @@ def colormap(): @pytest.fixture -def colormapDialog(qapp, qapp_utils): +def colormapDialog(qapp): dialog = ColormapDialog.ColormapDialog() dialog.setAttribute(qt.Qt.WA_DeleteOnClose) yield weakref.proxy(dialog) @@ -59,6 +58,7 @@ def colormapDialog(qapp, qapp_utils): from silx.gui.qt import inspect if inspect.isValid(dialog): dialog.close() + del dialog qapp.processEvents() @@ -85,6 +85,7 @@ class TestColormapDialog(TestCaseQt, ParametricTestCase): modification are correctly updated if an other colormapdialog is editing the same colormap""" colormapDiag2 = ColormapDialog.ColormapDialog() + colormapDiag2.setAttribute(qt.Qt.WA_DeleteOnClose) colormapDiag2.setColormap(self.colormap) colormapDiag2.show() self.colormapDiag.setColormap(self.colormap) @@ -106,6 +107,8 @@ class TestColormapDialog(TestCaseQt, ParametricTestCase): self.assertTrue(int(colormapDiag2._minValue.getValue()) == 10) self.assertTrue(int(colormapDiag2._maxValue.getValue()) == 20) colormapDiag2.close() + del colormapDiag2 + self.qapp.processEvents() def testGUIModalOk(self): """Make sure the colormap is modified if gone through accept""" @@ -214,8 +217,8 @@ class TestColormapDialog(TestCaseQt, ParametricTestCase): self.assertTrue(self.colormapDiag._minValue.isEnabled()) self.assertTrue(self.colormapDiag._maxValue.isEnabled()) else: - self.assertFalse(self.colormapDiag._minValue._numVal.isEnabled()) - self.assertFalse(self.colormapDiag._maxValue._numVal.isEnabled()) + self.assertTrue(self.colormapDiag._minValue._numVal.isReadOnly()) + self.assertTrue(self.colormapDiag._maxValue._numVal.isReadOnly()) def testColormapDel(self): """Check behavior if the colormap has been deleted outside. For now @@ -244,13 +247,14 @@ class TestColormapDialog(TestCaseQt, ParametricTestCase): self.colormap.setVRange(11, 201) self.assertTrue(self.colormapDiag._minValue.getValue() == 11) self.assertTrue(self.colormapDiag._maxValue.getValue() == 201) - self.assertTrue(self.colormapDiag._minValue._numVal.isEnabled()) - self.assertTrue(self.colormapDiag._maxValue._numVal.isEnabled()) + self.assertFalse(self.colormapDiag._minValue._numVal.isReadOnly()) + self.assertFalse(self.colormapDiag._maxValue._numVal.isReadOnly()) self.assertFalse(self.colormapDiag._minValue.isAutoChecked()) self.assertFalse(self.colormapDiag._maxValue.isAutoChecked()) self.colormap.setVRange(None, None) - self.assertFalse(self.colormapDiag._minValue._numVal.isEnabled()) - self.assertFalse(self.colormapDiag._maxValue._numVal.isEnabled()) + self.qapp.processEvents() + self.assertTrue(self.colormapDiag._minValue._numVal.isReadOnly()) + self.assertTrue(self.colormapDiag._maxValue._numVal.isReadOnly()) self.assertTrue(self.colormapDiag._minValue.isAutoChecked()) self.assertTrue(self.colormapDiag._maxValue.isAutoChecked()) diff --git a/src/silx/gui/dialog/test/test_datafiledialog.py b/src/silx/gui/dialog/test/test_datafiledialog.py index 8411c67..32d75c2 100644 --- a/src/silx/gui/dialog/test/test_datafiledialog.py +++ b/src/silx/gui/dialog/test/test_datafiledialog.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -89,7 +88,13 @@ def setUpModule(): def tearDownModule(): global _tmpDirectory - shutil.rmtree(_tmpDirectory) + for _ in range(10): + try: + shutil.rmtree(_tmpDirectory) + except PermissionError: # Might fail on appveyor + testutils.TestCaseQt.qWait(500) + else: + break _tmpDirectory = None @@ -491,7 +496,7 @@ class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): for i in range(model.rowCount(rootIndex)): index = model.index(i, 0, rootIndex) flags = model.flags(index) - isEnabled = (int(flags) & qt.Qt.ItemIsEnabled) != 0 + isEnabled = flags & qt.Qt.ItemIsEnabled == qt.Qt.ItemIsEnabled if isEnabled: selectable += 1 return selectable diff --git a/src/silx/gui/dialog/test/test_imagefiledialog.py b/src/silx/gui/dialog/test/test_imagefiledialog.py index 9e204b9..79c12ed 100644 --- a/src/silx/gui/dialog/test/test_imagefiledialog.py +++ b/src/silx/gui/dialog/test/test_imagefiledialog.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -96,7 +95,13 @@ def setUpModule(): def tearDownModule(): global _tmpDirectory - shutil.rmtree(_tmpDirectory) + for _ in range(10): + try: + shutil.rmtree(_tmpDirectory) + except PermissionError: # Might fail on appveyor + testutils.TestCaseQt.qWait(500) + else: + break _tmpDirectory = None @@ -516,7 +521,7 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): for i in range(model.rowCount(rootIndex)): index = model.index(i, 0, rootIndex) flags = model.flags(index) - isEnabled = (int(flags) & qt.Qt.ItemIsEnabled) != 0 + isEnabled = flags & qt.Qt.ItemIsEnabled == qt.Qt.ItemIsEnabled if isEnabled: selectable += 1 return selectable diff --git a/src/silx/gui/dialog/utils.py b/src/silx/gui/dialog/utils.py index 4c48930..e07cf9f 100644 --- a/src/silx/gui/dialog/utils.py +++ b/src/silx/gui/dialog/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/fit/BackgroundWidget.py b/src/silx/gui/fit/BackgroundWidget.py index 7703ee1..9ab63e4 100644 --- a/src/silx/gui/fit/BackgroundWidget.py +++ b/src/silx/gui/fit/BackgroundWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2004-2021 V.A. Sole, European Synchrotron Radiation Facility # diff --git a/src/silx/gui/fit/FitConfig.py b/src/silx/gui/fit/FitConfig.py index 48ebca2..09dbfaa 100644 --- a/src/silx/gui/fit/FitConfig.py +++ b/src/silx/gui/fit/FitConfig.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2004-2021 V.A. Sole, European Synchrotron Radiation Facility # diff --git a/src/silx/gui/fit/FitWidget.py b/src/silx/gui/fit/FitWidget.py index 52ecafe..88f95cf 100644 --- a/src/silx/gui/fit/FitWidget.py +++ b/src/silx/gui/fit/FitWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/fit/FitWidgets.py b/src/silx/gui/fit/FitWidgets.py index 0fcc6b7..7bcf28c 100644 --- a/src/silx/gui/fit/FitWidgets.py +++ b/src/silx/gui/fit/FitWidgets.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2004-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/gui/fit/Parameters.py b/src/silx/gui/fit/Parameters.py index daa72f3..e9601a8 100644 --- a/src/silx/gui/fit/Parameters.py +++ b/src/silx/gui/fit/Parameters.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2004-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/gui/fit/__init__.py b/src/silx/gui/fit/__init__.py index e4fd3ab..478ea22 100644 --- a/src/silx/gui/fit/__init__.py +++ b/src/silx/gui/fit/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/gui/fit/setup.py b/src/silx/gui/fit/setup.py deleted file mode 100644 index 6672363..0000000 --- a/src/silx/gui/fit/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["P. Knobel"] -__license__ = "MIT" -__date__ = "21/07/2016" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('fit', parent_package, top_path) - config.add_subpackage('test') - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/gui/fit/test/__init__.py b/src/silx/gui/fit/test/__init__.py index 71128fb..b03339f 100644 --- a/src/silx/gui/fit/test/__init__.py +++ b/src/silx/gui/fit/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/fit/test/testBackgroundWidget.py b/src/silx/gui/fit/test/testBackgroundWidget.py index b8570f7..353d3d5 100644 --- a/src/silx/gui/fit/test/testBackgroundWidget.py +++ b/src/silx/gui/fit/test/testBackgroundWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/fit/test/testFitConfig.py b/src/silx/gui/fit/test/testFitConfig.py index 53da2dd..114ff62 100644 --- a/src/silx/gui/fit/test/testFitConfig.py +++ b/src/silx/gui/fit/test/testFitConfig.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/fit/test/testFitWidget.py b/src/silx/gui/fit/test/testFitWidget.py index abe9d89..fe61268 100644 --- a/src/silx/gui/fit/test/testFitWidget.py +++ b/src/silx/gui/fit/test/testFitWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/Hdf5Formatter.py b/src/silx/gui/hdf5/Hdf5Formatter.py index 6c3de41..4dbb0fc 100644 --- a/src/silx/gui/hdf5/Hdf5Formatter.py +++ b/src/silx/gui/hdf5/Hdf5Formatter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/Hdf5HeaderView.py b/src/silx/gui/hdf5/Hdf5HeaderView.py index 7255ce0..6d306e5 100644 --- a/src/silx/gui/hdf5/Hdf5HeaderView.py +++ b/src/silx/gui/hdf5/Hdf5HeaderView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/Hdf5Item.py b/src/silx/gui/hdf5/Hdf5Item.py index e07f835..8f20649 100755 --- a/src/silx/gui/hdf5/Hdf5Item.py +++ b/src/silx/gui/hdf5/Hdf5Item.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility @@ -31,6 +30,7 @@ __date__ = "17/01/2019" import logging import collections import enum +from typing import Optional from .. import qt from .. import icons @@ -62,10 +62,21 @@ class Hdf5Item(Hdf5Node): tree structure. """ - def __init__(self, text, obj, parent, key=None, h5Class=None, linkClass=None, populateAll=False): + def __init__( + self, + text: Optional[str], + obj, + parent, + key=None, + h5Class=None, + linkClass=None, + populateAll=False, + openedPath: Optional[str] = None, + ): """ - :param str text: text displayed + :param text: text displayed :param object obj: Pointer to a h5py-link object. See the `obj` attribute. + :param openedPath: The path with which the item was opened if any """ self.__obj = obj self.__key = key @@ -76,7 +87,7 @@ class Hdf5Item(Hdf5Node): self.__linkClass = linkClass self.__description = None self.__nx_class = None - Hdf5Node.__init__(self, parent, populateAll=populateAll) + Hdf5Node.__init__(self, parent, populateAll=populateAll, openedPath=openedPath) def _getCanonicalName(self): parent = self.parent @@ -209,7 +220,7 @@ class Hdf5Item(Hdf5Node): self.__isBroken = True else: self.__obj = obj - if not self.isGroupObj(): + if silx.io.utils.get_h5_class(obj) not in [silx.io.utils.H5Type.GROUP, silx.io.utils.H5Type.FILE]: try: # pre-fetch of the data if obj.shape is None: diff --git a/src/silx/gui/hdf5/Hdf5LoadingItem.py b/src/silx/gui/hdf5/Hdf5LoadingItem.py index f11d252..70d015c 100644 --- a/src/silx/gui/hdf5/Hdf5LoadingItem.py +++ b/src/silx/gui/hdf5/Hdf5LoadingItem.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -27,6 +26,7 @@ __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "06/07/2018" +from typing import Optional from .. import qt from .Hdf5Node import Hdf5Node @@ -39,9 +39,15 @@ class Hdf5LoadingItem(Hdf5Node): At the end of the loading this item is replaced by the loaded one. """ - def __init__(self, text, parent, animatedIcon): + def __init__( + self, + text, + parent, + animatedIcon, + openedPath: Optional[str] = None, + ): """Constructor""" - Hdf5Node.__init__(self, parent) + Hdf5Node.__init__(self, parent, openedPath=openedPath) self.__text = text self.__animatedIcon = animatedIcon self.__animatedIcon.register(self) diff --git a/src/silx/gui/hdf5/Hdf5Node.py b/src/silx/gui/hdf5/Hdf5Node.py index be16535..0d58748 100644 --- a/src/silx/gui/hdf5/Hdf5Node.py +++ b/src/silx/gui/hdf5/Hdf5Node.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -28,6 +27,7 @@ __license__ = "MIT" __date__ = "24/07/2018" import weakref +from typing import Optional class Hdf5Node(object): @@ -36,16 +36,24 @@ class Hdf5Node(object): It provides link to the childs and to the parents, and a link to an external object. """ - def __init__(self, parent=None, populateAll=False): + def __init__( + self, + parent=None, + populateAll=False, + openedPath: Optional[str]=None, + ): """ Constructor :param Hdf5Node parent: Parent of the node, if exists, else None :param bool populateAll: If true, populate all the tree node. Else everything is lazy loaded. + :param openedPath: + The url or filename the node was created from, None if not directly created """ self.__child = None self.__parent = None + self.__openedPath = openedPath if parent is not None: self.__parent = weakref.ref(parent) if populateAll: @@ -60,6 +68,11 @@ class Hdf5Node(object): return "%s/?" % (parent._getCanonicalName()) @property + def _openedPath(self) -> Optional[str]: + """url or filename the node was created from, None if not directly created""" + return self.__openedPath + + @property def parent(self): """Parent of the node, or None if the node is a root diff --git a/src/silx/gui/hdf5/Hdf5TreeModel.py b/src/silx/gui/hdf5/Hdf5TreeModel.py index a32f7cf..8ac800a 100644 --- a/src/silx/gui/hdf5/Hdf5TreeModel.py +++ b/src/silx/gui/hdf5/Hdf5TreeModel.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -30,6 +29,7 @@ __date__ = "12/03/2019" import os import logging +from typing import Optional import functools from .. import qt from .. import icons @@ -39,6 +39,9 @@ from .Hdf5LoadingItem import Hdf5LoadingItem from . import _utils from ... import io as silx_io +import h5py + + _logger = logging.getLogger(__name__) @@ -98,7 +101,13 @@ class LoadingItemRunnable(qt.QRunnable): :rtpye: Hdf5Node """ text = _createRootLabel(h5obj) - item = Hdf5Item(text=text, obj=h5obj, parent=oldItem.parent, populateAll=True) + item = Hdf5Item( + text=text, + obj=h5obj, + parent=oldItem.parent, + populateAll=True, + openedPath=oldItem._openedPath, + ) return item def run(self): @@ -554,10 +563,25 @@ class Hdf5TreeModel(qt.QAbstractItemModel): filename = node.obj.filename self.insertFileAsync(filename, index.row(), synchronizingNode=node) + @staticmethod + def __areH5pyObjectEqual(obj1, obj2): + """Compare commonh5/h5py object without comparing data""" + if isinstance(obj1, h5py.HLObject): # Priority to h5py __eq__ + return obj1 == obj2 + + # else compare commonh5 objects + if not isinstance(obj2, type(obj1)): + return False + def key(item): + if item.file is None: + return item.name + return item.file.filename, item.file.mode, item.name + return key(obj1) == key(obj2) + def h5pyObjectRow(self, h5pyObject): for row in range(self.__root.childCount()): item = self.__root.child(row) - if item.obj == h5pyObject: + if self.__areH5pyObjectEqual(item.obj, h5pyObject): return row return -1 @@ -572,7 +596,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel): index = 0 while index < self.__root.childCount(): item = self.__root.child(index) - if item.obj == h5pyObject: + if self.__areH5pyObjectEqual(item.obj, h5pyObject): qindex = self.index(index, 0, qt.QModelIndex()) self.synchronizeIndex(qindex) index += 1 @@ -602,13 +626,19 @@ class Hdf5TreeModel(qt.QAbstractItemModel): index = 0 while index < self.__root.childCount(): item = self.__root.child(index) - if item.obj == h5pyObject: + if self.__areH5pyObjectEqual(item.obj, h5pyObject): qindex = self.index(index, 0, qt.QModelIndex()) self.removeIndex(qindex) else: index += 1 - def insertH5pyObject(self, h5pyObject, text=None, row=-1): + def insertH5pyObject( + self, + h5pyObject, + text: Optional[str] = None, + row: int = -1, + filename: Optional[str] = None, + ): """Append an HDF5 object from h5py to the tree. :param h5pyObject: File handle/descriptor for a :class:`h5py.File` @@ -618,7 +648,15 @@ class Hdf5TreeModel(qt.QAbstractItemModel): text = _createRootLabel(h5pyObject) if row == -1: row = self.__root.childCount() - self.insertNode(row, Hdf5Item(text=text, obj=h5pyObject, parent=self.__root)) + self.insertNode( + row, + Hdf5Item( + text=text, + obj=h5pyObject, + parent=self.__root, + openedPath=filename, + ) + ) def hasPendingOperations(self): return len(self.__runnerSet) > 0 @@ -630,7 +668,12 @@ class Hdf5TreeModel(qt.QAbstractItemModel): # create temporary item if synchronizingNode is None: text = os.path.basename(filename) - item = Hdf5LoadingItem(text=text, parent=self.__root, animatedIcon=self.__animatedIcon) + item = Hdf5LoadingItem( + text=text, + parent=self.__root, + animatedIcon=self.__animatedIcon, + openedPath=filename, + ) self.insertNode(row, item) else: item = synchronizingNode @@ -655,7 +698,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel): if self.__ownFiles: self.__openedFiles.append(h5file) self.sigH5pyObjectLoaded.emit(h5file) - self.insertH5pyObject(h5file, row=row) + self.insertH5pyObject(h5file, row=row, filename=filename) except IOError: _logger.debug("File '%s' can't be read.", filename, exc_info=True) raise diff --git a/src/silx/gui/hdf5/Hdf5TreeView.py b/src/silx/gui/hdf5/Hdf5TreeView.py index b276618..da35d15 100644 --- a/src/silx/gui/hdf5/Hdf5TreeView.py +++ b/src/silx/gui/hdf5/Hdf5TreeView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/NexusSortFilterProxyModel.py b/src/silx/gui/hdf5/NexusSortFilterProxyModel.py index 9c3533f..1b80c3e 100644 --- a/src/silx/gui/hdf5/NexusSortFilterProxyModel.py +++ b/src/silx/gui/hdf5/NexusSortFilterProxyModel.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/__init__.py b/src/silx/gui/hdf5/__init__.py index 1b5a602..2243484 100644 --- a/src/silx/gui/hdf5/__init__.py +++ b/src/silx/gui/hdf5/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/_utils.py b/src/silx/gui/hdf5/_utils.py index 8f32252..1d1b4cb 100644 --- a/src/silx/gui/hdf5/_utils.py +++ b/src/silx/gui/hdf5/_utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/setup.py b/src/silx/gui/hdf5/setup.py deleted file mode 100644 index 786a851..0000000 --- a/src/silx/gui/hdf5/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "28/09/2016" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('hdf5', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/gui/hdf5/test/__init__.py b/src/silx/gui/hdf5/test/__init__.py index 71128fb..b03339f 100644 --- a/src/silx/gui/hdf5/test/__init__.py +++ b/src/silx/gui/hdf5/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/hdf5/test/test_hdf5.py b/src/silx/gui/hdf5/test/test_hdf5.py index 9b1b88a..6e77e1d 100755 --- a/src/silx/gui/hdf5/test/test_hdf5.py +++ b/src/silx/gui/hdf5/test/test_hdf5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/icons.py b/src/silx/gui/icons.py index 1493b92..b7a9000 100644 --- a/src/silx/gui/icons.py +++ b/src/silx/gui/icons.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/AlphaSlider.py b/src/silx/gui/plot/AlphaSlider.py index da55b1e..486ca6f 100644 --- a/src/silx/gui/plot/AlphaSlider.py +++ b/src/silx/gui/plot/AlphaSlider.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ColorBar.py b/src/silx/gui/plot/ColorBar.py index 8cafc06..247da07 100644 --- a/src/silx/gui/plot/ColorBar.py +++ b/src/silx/gui/plot/ColorBar.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -590,7 +589,7 @@ class _ColorScale(qt.QWidget): def mouseMoveEvent(self, event): tooltip = str(self.getValueFromRelativePosition( - self._getRelativePosition(event.y()))) + self._getRelativePosition(qt.getMouseEventPosition(event)[1]))) qt.QToolTip.showText(event.globalPos(), tooltip, self) super(_ColorScale, self).mouseMoveEvent(event) diff --git a/src/silx/gui/plot/Colormap.py b/src/silx/gui/plot/Colormap.py index 22fea7f..8eaee84 100644 --- a/src/silx/gui/plot/Colormap.py +++ b/src/silx/gui/plot/Colormap.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ColormapDialog.py b/src/silx/gui/plot/ColormapDialog.py index 7c66cb8..0c0df2c 100644 --- a/src/silx/gui/plot/ColormapDialog.py +++ b/src/silx/gui/plot/ColormapDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Deprecated module providing ColormapDialog.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent", "H.Payno"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot/Colors.py b/src/silx/gui/plot/Colors.py index 277e104..34ee815 100644 --- a/src/silx/gui/plot/Colors.py +++ b/src/silx/gui/plot/Colors.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2017 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Color conversion function, color dictionary and colormap tools.""" -from __future__ import absolute_import - __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" __date__ = "14/06/2018" diff --git a/src/silx/gui/plot/CompareImages.py b/src/silx/gui/plot/CompareImages.py index 857fc79..80e0db3 100644 --- a/src/silx/gui/plot/CompareImages.py +++ b/src/silx/gui/plot/CompareImages.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ComplexImageView.py b/src/silx/gui/plot/ComplexImageView.py index 4eee3b0..7febd19 100644 --- a/src/silx/gui/plot/ComplexImageView.py +++ b/src/silx/gui/plot/ComplexImageView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -28,8 +27,6 @@ The :class:`ComplexImageView` widget is dedicated to visualize a single 2D datas of complex data. """ -from __future__ import absolute_import - __authors__ = ["Vincent Favre-Nicolin", "T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot/CurvesROIWidget.py b/src/silx/gui/plot/CurvesROIWidget.py index 132d398..f0cc7f3 100644 --- a/src/silx/gui/plot/CurvesROIWidget.py +++ b/src/silx/gui/plot/CurvesROIWidget.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -175,8 +174,8 @@ class CurvesROIWidget(qt.QWidget): self._isConnected = False # True if connected to plot signals self._isInit = False - # expose API - self.getROIListAndDict = self.roiTable.getROIListAndDict + def getROIListAndDict(self): + return self.roiTable.getROIListAndDict() def getPlotWidget(self): """Returns the associated PlotWidget or None @@ -1568,14 +1567,6 @@ class CurvesROIDockWidget(qt.QDockWidget): action.setIcon(icons.getQIcon('plot-roi')) return action - def showEvent(self, event): - """Make sure this widget is raised when it is shown - (when it is first created as a tab in PlotWindow or when it is shown - again after hiding). - """ - self.raise_() - qt.QDockWidget.showEvent(self, event) - @property def currentROI(self): return self.roiWidget.currentRoi diff --git a/src/silx/gui/plot/ImageStack.py b/src/silx/gui/plot/ImageStack.py index 1588a31..e2bed9d 100644 --- a/src/silx/gui/plot/ImageStack.py +++ b/src/silx/gui/plot/ImageStack.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ImageView.py b/src/silx/gui/plot/ImageView.py index f8b830a..a451b2d 100644 --- a/src/silx/gui/plot/ImageView.py +++ b/src/silx/gui/plot/ImageView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2021 European Synchrotron Radiation Facility @@ -37,9 +36,6 @@ Basic usage of :class:`ImageView` is through the following methods: For an example of use, see `imageview.py` in :ref:`sample-code`. """ -from __future__ import division - - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "26/04/2018" diff --git a/src/silx/gui/plot/Interaction.py b/src/silx/gui/plot/Interaction.py index 6213889..053fbe5 100644 --- a/src/silx/gui/plot/Interaction.py +++ b/src/silx/gui/plot/Interaction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ItemsSelectionDialog.py b/src/silx/gui/plot/ItemsSelectionDialog.py index c0504b0..c303c6b 100644 --- a/src/silx/gui/plot/ItemsSelectionDialog.py +++ b/src/silx/gui/plot/ItemsSelectionDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/LegendSelector.py b/src/silx/gui/plot/LegendSelector.py index d439387..4d8ebe9 100755 --- a/src/silx/gui/plot/LegendSelector.py +++ b/src/silx/gui/plot/LegendSelector.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -1030,10 +1029,3 @@ class LegendsDockWidget(qt.QDockWidget): self.plot.sigContentChanged.disconnect(self.updateLegends) self.plot.sigActiveCurveChanged.disconnect(self.updateLegends) self._isConnected = False - - def showEvent(self, event): - """Make sure this widget is raised when it is shown - (when it is first created as a tab in PlotWindow or when it is shown - again after hiding). - """ - self.raise_() diff --git a/src/silx/gui/plot/LimitsHistory.py b/src/silx/gui/plot/LimitsHistory.py index a323548..7215e37 100644 --- a/src/silx/gui/plot/LimitsHistory.py +++ b/src/silx/gui/plot/LimitsHistory.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/MaskToolsWidget.py b/src/silx/gui/plot/MaskToolsWidget.py index 522be48..46b532c 100644 --- a/src/silx/gui/plot/MaskToolsWidget.py +++ b/src/silx/gui/plot/MaskToolsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -30,7 +29,6 @@ This widget is meant to work with :class:`silx.gui.plot.PlotWidget`. - :class:`MaskToolsWidget`: GUI for :class:`Mask` - :class:`MaskToolsDockWidget`: DockWidget to integrate in :class:`PlotWindow` """ -from __future__ import division __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" diff --git a/src/silx/gui/plot/PlotActions.py b/src/silx/gui/plot/PlotActions.py index dd16221..f32be3c 100644 --- a/src/silx/gui/plot/PlotActions.py +++ b/src/silx/gui/plot/PlotActions.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/PlotEvents.py b/src/silx/gui/plot/PlotEvents.py index 83f253c..be875d7 100644 --- a/src/silx/gui/plot/PlotEvents.py +++ b/src/silx/gui/plot/PlotEvents.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/PlotInteraction.py b/src/silx/gui/plot/PlotInteraction.py index 6ebe6b1..c4d64a5 100644 --- a/src/silx/gui/plot/PlotInteraction.py +++ b/src/silx/gui/plot/PlotInteraction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/PlotToolButtons.py b/src/silx/gui/plot/PlotToolButtons.py index 3970896..a810ce1 100644 --- a/src/silx/gui/plot/PlotToolButtons.py +++ b/src/silx/gui/plot/PlotToolButtons.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/PlotTools.py b/src/silx/gui/plot/PlotTools.py index 5929473..35d0f48 100644 --- a/src/silx/gui/plot/PlotTools.py +++ b/src/silx/gui/plot/PlotTools.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """Set of widgets to associate with a :class:'PlotWidget'. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "01/03/2018" diff --git a/src/silx/gui/plot/PlotWidget.py b/src/silx/gui/plot/PlotWidget.py index 6cb5ef5..f07ef30 100755 --- a/src/silx/gui/plot/PlotWidget.py +++ b/src/silx/gui/plot/PlotWidget.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -26,9 +25,6 @@ The :class:`PlotWidget` implements the plot API initially provided in PyMca. """ -from __future__ import division - - __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" __date__ = "21/12/2018" @@ -42,6 +38,7 @@ from collections import OrderedDict, namedtuple from contextlib import contextmanager import datetime as dt import itertools +import numbers import typing import warnings @@ -902,10 +899,13 @@ class PlotWidget(qt.QMainWindow): 'image': (items.ImageBase,), 'scatter': (items.Scatter,), 'marker': (items.MarkerBase,), - 'item': (items.Shape, - items.BoundingRect, - items.XAxisExtent, - items.YAxisExtent), + 'item': ( + items.Line, + items.Shape, + items.BoundingRect, + items.XAxisExtent, + items.YAxisExtent, + ), 'histogram': (items.Histogram,), } """Mapping kind to item classes of this kind""" @@ -1130,8 +1130,8 @@ class PlotWidget(qt.QMainWindow): :type xerror: A float, or a numpy.ndarray of float32. If it is an array, it can either be a 1D array of same length as the data or a 2D array with 2 rows - of same length as the data: row 0 for positive errors, - row 1 for negative errors. + of same length as the data: row 0 for lower errors, + row 1 for upper errors. :param yerror: Values with the uncertainties on the y values :type yerror: A float, or a numpy.ndarray of float32. See xerror. :param int z: Layer on which to draw the curve (default: 1) @@ -1540,8 +1540,8 @@ class PlotWidget(qt.QMainWindow): :type xerror: A float, or a numpy.ndarray of float32. If it is an array, it can either be a 1D array of same length as the data or a 2D array with 2 rows - of same length as the data: row 0 for positive errors, - row 1 for negative errors. + of same length as the data: row 0 for lower errors, + row 1 for upper errors. :param yerror: Values with the uncertainties on the y values :type yerror: A float, or a numpy.ndarray of float32. See xerror. :param int z: Layer on which to draw the scatter (default: 1) @@ -3261,10 +3261,13 @@ class PlotWidget(qt.QMainWindow): def dataToPixel(self, x=None, y=None, axis="left", check=True): """Convert a position in data coordinates to a position in pixels. - :param float x: The X coordinate in data space. If None (default) - the middle position of the displayed data is used. - :param float y: The Y coordinate in data space. If None (default) - the middle position of the displayed data is used. + :param x: The X coordinate in data space. If None (default) + the middle position of the displayed data is used. + :type x: float or 1D numpy array of float + :param y: The Y coordinate in data space. If None (default) + the middle position of the displayed data is used. + :type y: float or 1D numpy array of float + :param str axis: The Y axis to use for the conversion ('left' or 'right'). :param bool check: True to return None if outside displayed area, @@ -3272,7 +3275,7 @@ class PlotWidget(qt.QMainWindow): :returns: The corresponding position in pixels or None if the data position is not in the displayed area and check is True. - :rtype: A tuple of 2 floats: (xPixel, yPixel) or None. + :rtype: A tuple of 2 floats or 2 arrays of float: (xPixel, yPixel) or None. """ assert axis in ("left", "right") @@ -3285,12 +3288,26 @@ class PlotWidget(qt.QMainWindow): if y is None: y = 0.5 * (ymax + ymin) + if isinstance(x, numbers.Real) != isinstance(y, numbers.Real): + raise ValueError("x and y must be of the same type") + if not isinstance(x, numbers.Real) and (x.shape != y.shape or x.ndim != 1): + raise ValueError("x and y must be 1D arrays of the same length") + if check: - if x > xmax or x < xmin: - return None + isOutside = numpy.logical_or( + numpy.logical_or(x > xmax, x < xmin), + numpy.logical_or(y > ymax, y < ymin) + ) - if y > ymax or y < ymin: - return None + if numpy.any(isOutside): + if isinstance(x, numbers.Real): + return None + else: # Filter-out points that are outside + x = numpy.array(x, copy=True, dtype=numpy.float64) + x[isOutside] = numpy.nan + + y = numpy.array(y, copy=True, dtype=numpy.float64) + y[isOutside] = numpy.nan return self._backend.dataToPixel(x, y, axis=axis) @@ -3318,7 +3335,10 @@ class PlotWidget(qt.QMainWindow): if check: left, top, width, height = self.getPlotBoundsInPixels() - if not (left <= x <= left + width and top <= y <= top + height): + isOutside = numpy.logical_or( + numpy.logical_or(x < left, x > left + width), + numpy.logical_or(y < top, y > top + height)) + if numpy.any(isOutside): return None return self._backend.pixelToData(x, y, axis) @@ -3603,7 +3623,7 @@ class PlotWidget(qt.QMainWindow): qapp = qt.QApplication.instance() event = qt.QMouseEvent( qt.QEvent.MouseMove, - self.getWidgetHandle().mapFromGlobal(qt.QCursor.pos()), + qt.QPointF(self.getWidgetHandle().mapFromGlobal(qt.QCursor.pos())), qt.Qt.NoButton, qapp.mouseButtons(), qapp.keyboardModifiers()) diff --git a/src/silx/gui/plot/PlotWindow.py b/src/silx/gui/plot/PlotWindow.py index 0349585..e8da174 100644 --- a/src/silx/gui/plot/PlotWindow.py +++ b/src/silx/gui/plot/PlotWindow.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -215,7 +214,6 @@ class PlotWindow(PlotWidget): # lazy loaded actions needed by the controlButton menu self._consoleAction = None - self._statsAction = None self._panWithArrowKeysAction = None self._crosshairAction = None @@ -401,14 +399,19 @@ class PlotWindow(PlotWidget): custom_banner=banner, parent=self) self.addTabbedDockWidget(self._consoleDockWidget) - # self._consoleDockWidget.setVisible(True) self._consoleDockWidget.toggleViewAction().toggled.connect( - self.getConsoleAction().setChecked) + self._consoleDockWidgetToggled) self._consoleDockWidget.setVisible(isChecked) - def _toggleStatsVisibility(self, isChecked=False): - self.getStatsWidget().parent().setVisible(isChecked) + def _consoleVisibilityTriggered(self, isChecked): + if isChecked and self.isVisible(): + self._consoleDockWidget.show() + self._consoleDockWidget.raise_() + + def _consoleDockWidgetToggled(self, isChecked): + if self.isVisible(): + self.getConsoleAction().setChecked(isChecked) def _createToolBar(self, title, parent): """Create a QToolBar from the QAction of the PlotWindow. @@ -522,6 +525,17 @@ class PlotWindow(PlotWidget): self._handleFirstDockWidgetShow) self.addTabbedDockWidget(dockWidget) + def _handleDockWidgetViewActionTriggered(self, checked): + if checked: + action = self.sender() + if action is None: + return + dockWidget = action.parent() + if dockWidget is None: + return + dockWidget.show() # Show needed here for raise to have an effect + dockWidget.raise_() + def getColorBarWidget(self): """Returns the embedded :class:`ColorBarWidget` widget. @@ -536,6 +550,8 @@ class PlotWindow(PlotWidget): if self._legendsDockWidget is None: self._legendsDockWidget = LegendsDockWidget(plot=self) self._legendsDockWidget.hide() + self._legendsDockWidget.toggleViewAction().triggered.connect( + self._handleDockWidgetViewActionTriggered) self._legendsDockWidget.visibilityChanged.connect( self._handleFirstDockWidgetShow) return self._legendsDockWidget @@ -547,6 +563,8 @@ class PlotWindow(PlotWidget): self._curvesROIDockWidget = CurvesROIDockWidget( plot=self, name='Regions Of Interest') self._curvesROIDockWidget.hide() + self._curvesROIDockWidget.toggleViewAction().triggered.connect( + self._handleDockWidgetViewActionTriggered) self._curvesROIDockWidget.visibilityChanged.connect( self._handleFirstDockWidgetShow) return self._curvesROIDockWidget @@ -568,6 +586,8 @@ class PlotWindow(PlotWidget): self._maskToolsDockWidget = MaskToolsDockWidget( plot=self, name='Mask') self._maskToolsDockWidget.hide() + self._maskToolsDockWidget.toggleViewAction().triggered.connect( + self._handleDockWidgetViewActionTriggered) self._maskToolsDockWidget.visibilityChanged.connect( self._handleFirstDockWidgetShow) return self._maskToolsDockWidget @@ -583,9 +603,9 @@ class PlotWindow(PlotWidget): self._statsDockWidget.layout().setContentsMargins(0, 0, 0, 0) statsWidget = BasicStatsWidget(parent=self, plot=self) self._statsDockWidget.setWidget(statsWidget) - statsWidget.sigVisibilityChanged.connect( - self.getStatsAction().setChecked) self._statsDockWidget.hide() + self._statsDockWidget.toggleViewAction().triggered.connect( + self._handleDockWidgetViewActionTriggered) self._statsDockWidget.visibilityChanged.connect( self._handleFirstDockWidgetShow) return self._statsDockWidget.widget() @@ -618,6 +638,8 @@ class PlotWindow(PlotWidget): self._consoleAction.setCheckable(True) if IPythonDockWidget is not None: self._consoleAction.toggled.connect(self._toggleConsoleVisibility) + self._consoleAction.triggered.connect(self._consoleVisibilityTriggered) + else: self._consoleAction.setEnabled(False) return self._consoleAction @@ -648,12 +670,7 @@ class PlotWindow(PlotWidget): return self._panWithArrowKeysAction def getStatsAction(self): - if self._statsAction is None: - self._statsAction = qt.QAction('Curves stats', self) - self._statsAction.setCheckable(True) - self._statsAction.setChecked(self.getStatsWidget().parent().isVisible()) - self._statsAction.toggled.connect(self._toggleStatsVisibility) - return self._statsAction + return self.getStatsWidget().parent().toggleViewAction() def getRoiAction(self): """QAction toggling curve ROI dock widget diff --git a/src/silx/gui/plot/PrintPreviewToolButton.py b/src/silx/gui/plot/PrintPreviewToolButton.py index 30967e4..9069ac3 100644 --- a/src/silx/gui/plot/PrintPreviewToolButton.py +++ b/src/silx/gui/plot/PrintPreviewToolButton.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -101,7 +100,6 @@ plots on the same page. The plots all instantiate a app.exec() """ -from __future__ import absolute_import import logging from io import StringIO diff --git a/src/silx/gui/plot/Profile.py b/src/silx/gui/plot/Profile.py index 7565155..bf793c8 100644 --- a/src/silx/gui/plot/Profile.py +++ b/src/silx/gui/plot/Profile.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ProfileMainWindow.py b/src/silx/gui/plot/ProfileMainWindow.py index ce56cfd..09a5b41 100644 --- a/src/silx/gui/plot/ProfileMainWindow.py +++ b/src/silx/gui/plot/ProfileMainWindow.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ROIStatsWidget.py b/src/silx/gui/plot/ROIStatsWidget.py index 32a1395..732c60f 100644 --- a/src/silx/gui/plot/ROIStatsWidget.py +++ b/src/silx/gui/plot/ROIStatsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/ScatterMaskToolsWidget.py b/src/silx/gui/plot/ScatterMaskToolsWidget.py index c242dfc..3f8c28b 100644 --- a/src/silx/gui/plot/ScatterMaskToolsWidget.py +++ b/src/silx/gui/plot/ScatterMaskToolsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -31,8 +30,6 @@ This widget is meant to work with a modified :class:`silx.gui.plot.PlotWidget` - :class:`ScatterMaskToolsDockWidget`: DockWidget to integrate in :class:`PlotWindow` """ -from __future__ import division - __authors__ = ["P. Knobel"] __license__ = "MIT" __date__ = "15/02/2019" diff --git a/src/silx/gui/plot/ScatterView.py b/src/silx/gui/plot/ScatterView.py index d3fd2e0..abacbef 100644 --- a/src/silx/gui/plot/ScatterView.py +++ b/src/silx/gui/plot/ScatterView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/StackView.py b/src/silx/gui/plot/StackView.py index 56793d7..5101f87 100644 --- a/src/silx/gui/plot/StackView.py +++ b/src/silx/gui/plot/StackView.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -230,7 +229,8 @@ class StackView(qt.QMainWindow): if silx.config.DEFAULT_PLOT_IMAGE_Y_AXIS_ORIENTATION == 'downward': self._plot.getYAxis().setInverted(True) - self._addColorBarAction() + self._plot.getColorBarAction().setVisible(True) + self._plot.getColorBarWidget().setVisible(True) self._profileToolBar = Profile3DToolBar(parent=self._plot, stackview=self) @@ -283,15 +283,6 @@ class StackView(qt.QMainWindow): signal=self.getStack(copy=False, returnNumpyArray=True)[0], signal_name="image_stack") - def _addColorBarAction(self): - self._plot.getColorBarWidget().setVisible(True) - actions = self._plot.toolBar().actions() - for index, action in enumerate(actions): - if action is self._plot.getColormapAction(): - break - self._colorbarAction = actions_control.ColorBarAction(self._plot, self._plot) - self._plot.toolBar().insertAction(actions[index + 1], self._colorbarAction) - def _plotCallback(self, eventDict): """Callback for plot events. @@ -1056,7 +1047,7 @@ class StackView(qt.QMainWindow): :rtype: QAction """ - return self._colorbarAction + return self._plot.getColorBarAction() def remove(self, legend=None, kind=('curve', 'image', 'item', 'marker')): diff --git a/src/silx/gui/plot/StatsWidget.py b/src/silx/gui/plot/StatsWidget.py index 00f78d0..b23946f 100644 --- a/src/silx/gui/plot/StatsWidget.py +++ b/src/silx/gui/plot/StatsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/_BaseMaskToolsWidget.py b/src/silx/gui/plot/_BaseMaskToolsWidget.py index 407ab11..dda75e1 100644 --- a/src/silx/gui/plot/_BaseMaskToolsWidget.py +++ b/src/silx/gui/plot/_BaseMaskToolsWidget.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2020 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -25,7 +24,6 @@ """This module is a collection of base classes used in modules :mod:`.MaskToolsWidget` (images) and :mod:`.ScatterMaskToolsWidget` """ -from __future__ import division __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" @@ -417,7 +415,7 @@ class BaseMaskToolsWidget(qt.QWidget): self._lastPencilPos = None self._multipleMasks = 'exclusive' - self._maskFileDir = qt.QDir.home().absolutePath() + self._maskFileDir = qt.QDir.current().absolutePath() self.plot.sigInteractiveModeChanged.connect( self._interactiveModeChanged) @@ -494,7 +492,7 @@ class BaseMaskToolsWidget(qt.QWidget): def maskFileDir(self): """The directory from which to load/save mask from/to files.""" if not os.path.isdir(self._maskFileDir): - self._maskFileDir = qt.QDir.home().absolutePath() + self._maskFileDir = qt.QDir.current().absolutePath() return self._maskFileDir @maskFileDir.setter @@ -632,7 +630,7 @@ class BaseMaskToolsWidget(qt.QWidget): invertAction.setText('Invert') icon = icons.getQIcon("mask-invert") invertAction.setIcon(icon) - invertAction.setShortcut(qt.Qt.CTRL + qt.Qt.Key_I) + invertAction.setShortcut(qt.QKeySequence(qt.Qt.CTRL | qt.Qt.Key_I)) invertAction.setToolTip('Invert current mask <b>%s</b>' % invertAction.shortcut().toString()) invertAction.triggered.connect(self._handleInvertMask) @@ -1273,10 +1271,3 @@ class BaseMaskToolsDockWidget(qt.QDockWidget): self.widget().setDirection(qt.QBoxLayout.LeftToRight) self.resize(self.widget().minimumSize()) self.adjustSize() - - def showEvent(self, event): - """Make sure this widget is raised when it is shown - (when it is first created as a tab in PlotWindow or when it is shown - again after hiding). - """ - self.raise_() diff --git a/src/silx/gui/plot/__init__.py b/src/silx/gui/plot/__init__.py index 3a141b3..129c4de 100644 --- a/src/silx/gui/plot/__init__.py +++ b/src/silx/gui/plot/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/_utils/__init__.py b/src/silx/gui/plot/_utils/__init__.py index ed87b18..39fa7e4 100644 --- a/src/silx/gui/plot/_utils/__init__.py +++ b/src/silx/gui/plot/_utils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/_utils/delaunay.py b/src/silx/gui/plot/_utils/delaunay.py index 49ad05f..48b0db7 100644 --- a/src/silx/gui/plot/_utils/delaunay.py +++ b/src/silx/gui/plot/_utils/delaunay.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -55,8 +54,7 @@ def delaunay(x, y): try: delaunay = _Delaunay(points) except (RuntimeError, ValueError): - _logger.error("Delaunay tesselation failed: %s", - sys.exc_info()[1]) + _logger.debug("Delaunay tesselation failed: %s", sys.exc_info()[1]) delaunay = None return delaunay diff --git a/src/silx/gui/plot/_utils/dtime_ticklayout.py b/src/silx/gui/plot/_utils/dtime_ticklayout.py index ebf775b..3c355d7 100644 --- a/src/silx/gui/plot/_utils/dtime_ticklayout.py +++ b/src/silx/gui/plot/_utils/dtime_ticklayout.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2018 European Synchrotron Radiation Facility +# Copyright (c) 2014-2022 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 @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module implements date-time labels layout on graph axes.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["P. Kenter"] __license__ = "MIT" __date__ = "04/04/2018" @@ -212,6 +209,7 @@ def addValueToDate(dateTime, value, unit): :param float value: value to be added :param DtUnit unit: of the value :return: + :raises ValueError: unit is unsupported or result is out of datetime bounds """ #logger.debug("addValueToDate({}, {}, {})".format(dateTime, value, unit)) @@ -362,6 +360,9 @@ def findStartDate(dMin, dMax, nTicks): else: niceVal = math.floor(dVal / niceSpacing) * niceSpacing + if unit == DtUnit.YEARS and niceVal <= dt.MINYEAR: + niceVal = max(1, niceSpacing) + _logger.debug("StartValue: dVal = {}, niceVal: {} ({})" .format(dVal, niceVal, unit.name)) @@ -394,7 +395,10 @@ def dateRange(dMin, dMax, step, unit, includeFirstBeyond = False): dateTime = dMin while dateTime < dMax: yield dateTime - dateTime = addValueToDate(dateTime, step, unit) + try: + dateTime = addValueToDate(dateTime, step, unit) + except ValueError: + return # current dateTime is out of datetime bounds if includeFirstBeyond: yield dateTime @@ -420,12 +424,6 @@ def calcTicks(dMin, dMax, nTicks): includeFirstBeyond=True): result.append(d) - assert result[0] <= dMin, \ - "First nice date ({}) should be <= dMin {}".format(result[0], dMin) - - assert result[-1] >= dMax, \ - "Last nice date ({}) should be >= dMax {}".format(result[-1], dMax) - return result, niceSpacing, unit diff --git a/src/silx/gui/plot/_utils/panzoom.py b/src/silx/gui/plot/_utils/panzoom.py index 77efd10..8592ad0 100644 --- a/src/silx/gui/plot/_utils/panzoom.py +++ b/src/silx/gui/plot/_utils/panzoom.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/_utils/setup.py b/src/silx/gui/plot/_utils/setup.py deleted file mode 100644 index 0271745..0000000 --- a/src/silx/gui/plot/_utils/setup.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "21/03/2017" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('_utils', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/gui/plot/_utils/test/__init__.py b/src/silx/gui/plot/_utils/test/__init__.py index 3ad225d..78821ec 100644 --- a/src/silx/gui/plot/_utils/test/__init__.py +++ b/src/silx/gui/plot/_utils/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/_utils/test/test_dtime_ticklayout.py b/src/silx/gui/plot/_utils/test/test_dtime_ticklayout.py index 8d35acf..87c0742 100644 --- a/src/silx/gui/plot/_utils/test/test_dtime_ticklayout.py +++ b/src/silx/gui/plot/_utils/test/test_dtime_ticklayout.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2015-2018 European Synchrotron Radiation Facility +# Copyright (c) 2015-2022 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 @@ -23,57 +22,67 @@ # # ###########################################################################*/ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["P. Kenter"] __license__ = "MIT" __date__ = "06/04/2018" import datetime as dt -import unittest +import pytest + + +from silx.gui.plot._utils.dtime_ticklayout import calcTicks, DtUnit, SECONDS_PER_YEAR -from silx.gui.plot._utils.dtime_ticklayout import ( - calcTicks, DtUnit, SECONDS_PER_YEAR) +def testSmallMonthlySpacing(): + """Tests a range that did result in a spacing of less than 1 month. + It is impossible to add fractional month so the unit must be in days + """ + from dateutil import parser + d1 = parser.parse("2017-01-03 13:15:06.000044") + d2 = parser.parse("2017-03-08 09:16:16.307584") + _ticks, _units, spacing = calcTicks(d1, d2, nTicks=4) -class TestTickLayout(unittest.TestCase): - """Test ticks layout algorithms""" + assert spacing == DtUnit.DAYS - def testSmallMonthlySpacing(self): - """ Tests a range that did result in a spacing of less than 1 month. - It is impossible to add fractional month so the unit must be in days - """ - from dateutil import parser - d1 = parser.parse("2017-01-03 13:15:06.000044") - d2 = parser.parse("2017-03-08 09:16:16.307584") - _ticks, _units, spacing = calcTicks(d1, d2, nTicks=4) - self.assertEqual(spacing, DtUnit.DAYS) +def testNoCrash(): + """Creates many combinations of and number-of-ticks and end-dates; + tests that it doesn't give an exception and returns a reasonable number + of ticks. + """ + d1 = dt.datetime(2017, 1, 3, 13, 15, 6, 44) + value = 100e-6 # Start at 100 micro sec range. - def testNoCrash(self): - """ Creates many combinations of and number-of-ticks and end-dates; - tests that it doesn't give an exception and returns a reasonable number - of ticks. - """ - d1 = dt.datetime(2017, 1, 3, 13, 15, 6, 44) + while value <= 200 * SECONDS_PER_YEAR: - value = 100e-6 # Start at 100 micro sec range. + d2 = d1 + dt.timedelta(microseconds=value * 1e6) # end date range - while value <= 200 * SECONDS_PER_YEAR: + for numTicks in range(2, 12): + ticks, _, _ = calcTicks(d1, d2, numTicks) - d2 = d1 + dt.timedelta(microseconds=value*1e6) # end date range + margin = 2.5 + assert ( + numTicks / margin <= len(ticks) <= numTicks * margin + ), "Condition {} <= {} <= {} failed for # ticks={} and d2={}:".format( + numTicks / margin, len(ticks), numTicks * margin, numTicks, d2 + ) - for numTicks in range(2, 12): - ticks, _, _ = calcTicks(d1, d2, numTicks) + value = value * 1.5 # let date period grow exponentially - margin = 2.5 - self.assertTrue( - numTicks/margin <= len(ticks) <= numTicks*margin, - "Condition {} <= {} <= {} failed for # ticks={} and d2={}:" - .format(numTicks/margin, len(ticks), numTicks * margin, - numTicks, d2)) - value = value * 1.5 # let date period grow exponentially +@pytest.mark.parametrize( + "dMin, dMax", + [ + (dt.datetime(1, 1, 1), dt.datetime(400, 1, 1)), + (dt.datetime(4000, 1, 1), dt.datetime(9999, 1, 1)), + (dt.datetime(1, 1, 1), dt.datetime(9999, 12, 23)), + ], +) +def testCalcTicksOutOfBoundTicks(dMin, dMax): + """Test tick generation with values leading to out-of-bound ticks""" + ticks, _, unit = calcTicks(dMin, dMax, nTicks=5) + assert len(ticks) != 0 + assert unit == DtUnit.YEARS diff --git a/src/silx/gui/plot/_utils/test/test_ticklayout.py b/src/silx/gui/plot/_utils/test/test_ticklayout.py index 884b71b..8388c7e 100644 --- a/src/silx/gui/plot/_utils/test/test_ticklayout.py +++ b/src/silx/gui/plot/_utils/test/test_ticklayout.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility @@ -23,8 +22,6 @@ # # ###########################################################################*/ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/01/2018" diff --git a/src/silx/gui/plot/_utils/ticklayout.py b/src/silx/gui/plot/_utils/ticklayout.py index c9fd3e6..4266be0 100644 --- a/src/silx/gui/plot/_utils/ticklayout.py +++ b/src/silx/gui/plot/_utils/ticklayout.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module implements labels layout on graph axes.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "18/10/2016" diff --git a/src/silx/gui/plot/actions/PlotAction.py b/src/silx/gui/plot/actions/PlotAction.py index 2983775..de041dc 100644 --- a/src/silx/gui/plot/actions/PlotAction.py +++ b/src/silx/gui/plot/actions/PlotAction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2017 European Synchrotron Radiation Facility @@ -27,9 +26,6 @@ The class :class:`.PlotAction` help the creation of a qt.QAction associated with a :class:`.PlotWidget`. """ -from __future__ import division - - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "03/01/2018" diff --git a/src/silx/gui/plot/actions/PlotToolAction.py b/src/silx/gui/plot/actions/PlotToolAction.py index fbb0b0f..8c3b3c2 100644 --- a/src/silx/gui/plot/actions/PlotToolAction.py +++ b/src/silx/gui/plot/actions/PlotToolAction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2020 European Synchrotron Radiation Facility @@ -27,9 +26,6 @@ The class :class:`.PlotToolAction` help the creation of a qt.QAction associating a tool window with a :class:`.PlotWidget`. """ -from __future__ import division - - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "10/10/2018" diff --git a/src/silx/gui/plot/actions/__init__.py b/src/silx/gui/plot/actions/__init__.py index 930c728..3e606c6 100644 --- a/src/silx/gui/plot/actions/__init__.py +++ b/src/silx/gui/plot/actions/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/actions/control.py b/src/silx/gui/plot/actions/control.py index 439985e..e75048a 100755 --- a/src/silx/gui/plot/actions/control.py +++ b/src/silx/gui/plot/actions/control.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2019 European Synchrotron Radiation Facility @@ -46,8 +45,6 @@ The following QAction are available: - :class:`ZoomOutAction` """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "27/11/2020" diff --git a/src/silx/gui/plot/actions/fit.py b/src/silx/gui/plot/actions/fit.py index e130b24..3489f70 100644 --- a/src/silx/gui/plot/actions/fit.py +++ b/src/silx/gui/plot/actions/fit.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -32,8 +31,6 @@ The following QAction are available: .. autoclass:`.FitAction` """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "10/10/2018" diff --git a/src/silx/gui/plot/actions/histogram.py b/src/silx/gui/plot/actions/histogram.py index be9f5a7..448dd55 100644 --- a/src/silx/gui/plot/actions/histogram.py +++ b/src/silx/gui/plot/actions/histogram.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -31,8 +30,6 @@ The following QAction are available: - :class:`PixelIntensitiesHistoAction` """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __date__ = "01/12/2020" __license__ = "MIT" @@ -64,7 +61,7 @@ class _ElidedLabel(ElidedLabel): def sizeHint(self): hint = super().sizeHint() - nbchar = max(len(self.getText()), 12) + nbchar = max(len(self.text()), 12) width = self.fontMetrics().boundingRect('#' * nbchar).width() return qt.QSize(max(hint.width(), width), hint.height()) diff --git a/src/silx/gui/plot/actions/io.py b/src/silx/gui/plot/actions/io.py index 7f4edd3..6cdd4d0 100644 --- a/src/silx/gui/plot/actions/io.py +++ b/src/silx/gui/plot/actions/io.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -33,8 +32,6 @@ The following QAction are available: - :class:`SaveAction` """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "25/09/2020" diff --git a/src/silx/gui/plot/actions/medfilt.py b/src/silx/gui/plot/actions/medfilt.py index f86a377..25fcdb2 100644 --- a/src/silx/gui/plot/actions/medfilt.py +++ b/src/silx/gui/plot/actions/medfilt.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2020 European Synchrotron Radiation Facility @@ -34,8 +33,6 @@ The following QAction are available: """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" diff --git a/src/silx/gui/plot/actions/mode.py b/src/silx/gui/plot/actions/mode.py index ee05256..7edc8bb 100644 --- a/src/silx/gui/plot/actions/mode.py +++ b/src/silx/gui/plot/actions/mode.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2018 European Synchrotron Radiation Facility @@ -32,8 +31,6 @@ The following QAction are available: - :class:`PanModeAction` """ -from __future__ import division - __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "16/08/2017" diff --git a/src/silx/gui/plot/backends/BackendBase.py b/src/silx/gui/plot/backends/BackendBase.py index 1e86807..d7653f3 100755 --- a/src/silx/gui/plot/backends/BackendBase.py +++ b/src/silx/gui/plot/backends/BackendBase.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -507,8 +506,10 @@ class BackendBase(object): """Convert a position in data space to a position in pixels in the widget. - :param float x: The X coordinate in data space. - :param float y: The Y coordinate in data space. + :param x: The X coordinate in data space. + :type x: float or sequence of float + :param y: The Y coordinate in data space. + :type y: float or sequence of float :param str axis: The Y axis to use for the conversion ('left' or 'right'). :returns: The corresponding position in pixels or diff --git a/src/silx/gui/plot/backends/BackendMatplotlib.py b/src/silx/gui/plot/backends/BackendMatplotlib.py index 7fe4ec0..8610ed1 100755 --- a/src/silx/gui/plot/backends/BackendMatplotlib.py +++ b/src/silx/gui/plot/backends/BackendMatplotlib.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -24,8 +23,6 @@ # ###########################################################################*/ """Matplotlib Plot backend.""" -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent, H. Payno"] __license__ = "MIT" __date__ = "21/12/2018" @@ -33,7 +30,7 @@ __date__ = "21/12/2018" import logging import datetime as dt -from typing import Tuple +from typing import Tuple, Union import numpy from pkg_resources import parse_version as _parse_version @@ -64,6 +61,7 @@ from . import BackendBase from .. import items from .._utils import FLOAT32_MINPOS from .._utils.dtime_ticklayout import calcTicks, bestFormatString, timestamp +from ...qt import inspect as qt_inspect _PATCH_LINESTYLE = { "-": 'solid', @@ -166,8 +164,13 @@ class NiceDateLocator(Locator): vmin, vmax = vmax, vmin # vmin and vmax should be timestamps (i.e. seconds since 1 Jan 1970) - dtMin = dt.datetime.fromtimestamp(vmin, tz=self.tz) - dtMax = dt.datetime.fromtimestamp(vmax, tz=self.tz) + try: + dtMin = dt.datetime.fromtimestamp(vmin, tz=self.tz) + dtMax = dt.datetime.fromtimestamp(vmax, tz=self.tz) + except ValueError: + _logger.warning("Data range cannot be displayed with time axis") + return [] + dtTicks, self._spacing, self._unit = \ calcTicks(dtMin, dtMax, self.numTicks) @@ -1220,7 +1223,11 @@ class BackendMatplotlib(BackendBase.BackendBase): """Compatibility wrapper for devicePixelRatioF""" return 1. - def _mplToQtPosition(self, x: float, y: float) -> Tuple[float, float]: + def _mplToQtPosition( + self, + x: Union[float,numpy.ndarray], + y: Union[float,numpy.ndarray] + ) -> Tuple[Union[float,numpy.ndarray], Union[float,numpy.ndarray]]: """Convert matplotlib "display" space coord to Qt widget logical pixel """ ratio = self._getDevicePixelRatio() @@ -1238,7 +1245,8 @@ class BackendMatplotlib(BackendBase.BackendBase): def dataToPixel(self, x, y, axis): ax = self.ax2 if axis == "right" else self.ax - displayPos = ax.transData.transform_point((x, y)).transpose() + points = numpy.transpose((x, y)) + displayPos = ax.transData.transform(points).transpose() return self._mplToQtPosition(*displayPos) def pixelToData(self, x, y, axis): @@ -1325,7 +1333,7 @@ class BackendMatplotlib(BackendBase.BackendBase): self._synchronizeForegroundColors() -class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): +class BackendMatplotlibQt(BackendMatplotlib, FigureCanvasQTAgg): """QWidget matplotlib backend using a QtAgg canvas. It adds fast overlay drawing and mouse event management. @@ -1516,6 +1524,10 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): self._drawOverlays() def replot(self): + if not qt_inspect.isValid(self): + _logger.info("replot requested but widget no longer exists") + return + with self._plot._paintContext(): BackendMatplotlib._replot(self) diff --git a/src/silx/gui/plot/backends/BackendOpenGL.py b/src/silx/gui/plot/backends/BackendOpenGL.py index f1a12af..d7e8346 100755 --- a/src/silx/gui/plot/backends/BackendOpenGL.py +++ b/src/silx/gui/plot/backends/BackendOpenGL.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2021 European Synchrotron Radiation Facility +# Copyright (c) 2014-2022 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 @@ -24,8 +23,6 @@ # ############################################################################*/ """OpenGL Plot backend.""" -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "21/12/2018" @@ -196,7 +193,7 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): So, the caller should not modify these arrays afterwards. """ - def __init__(self, plot, parent=None, f=qt.Qt.WindowFlags()): + def __init__(self, plot, parent=None, f=qt.Qt.Widget): glu.OpenGLWidget.__init__(self, parent, alphaBufferSize=8, depthBufferSize=0, @@ -205,6 +202,8 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): f=f) BackendBase.BackendBase.__init__(self, plot, parent) + self.__isOpenGLValid = False + self._backgroundColor = 1., 1., 1., 1. self._dataBackgroundColor = 1., 1., 1., 1. @@ -236,7 +235,11 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): # QWidget - _MOUSE_BTNS = {1: 'left', 2: 'right', 4: 'middle'} + _MOUSE_BTNS = { + qt.Qt.LeftButton: 'left', + qt.Qt.RightButton: 'right', + qt.Qt.MiddleButton: 'middle', + } def sizeHint(self): return qt.QSize(8 * 80, 6 * 80) # Mimic MatplotlibBackend @@ -244,12 +247,12 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): def mousePressEvent(self, event): if event.button() not in self._MOUSE_BTNS: return super(BackendOpenGL, self).mousePressEvent(event) - self._plot.onMousePress( - event.x(), event.y(), self._MOUSE_BTNS[event.button()]) + x, y = qt.getMouseEventPosition(event) + self._plot.onMousePress(x, y, self._MOUSE_BTNS[event.button()]) event.accept() def mouseMoveEvent(self, event): - qtPos = event.x(), event.y() + qtPos = qt.getMouseEventPosition(event) previousMousePosInPixels = self._mousePosInPixels if qtPos == self._mouseInPlotArea(*qtPos): @@ -270,17 +273,14 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): def mouseReleaseEvent(self, event): if event.button() not in self._MOUSE_BTNS: return super(BackendOpenGL, self).mouseReleaseEvent(event) - self._plot.onMouseRelease( - event.x(), event.y(), self._MOUSE_BTNS[event.button()]) + x, y = qt.getMouseEventPosition(event) + self._plot.onMouseRelease(x, y, self._MOUSE_BTNS[event.button()]) event.accept() def wheelEvent(self, event): delta = event.angleDelta().y() angleInDegrees = delta / 8. - if qt.BINDING == "PySide6": - x, y = event.position().x(), event.position().y() - else: - x, y = event.x(), event.y() + x, y = qt.getMouseEventPosition(event) self._plot.onMouseWheel(x, y, angleInDegrees) event.accept() @@ -290,7 +290,9 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): # OpenGLWidget API def initializeGL(self): - gl.testGL() + self.__isOpenGLValid = gl.testGL() + if not self.__isOpenGLValid: + return gl.glClearStencil(0) @@ -379,6 +381,9 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): self._renderOverlayGL() def paintGL(self): + if not self.__isOpenGLValid: + return + plot = self._plotRef() if plot is None: return @@ -422,7 +427,11 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): pixelOffset = 3 context = glutils.RenderContext( - isXLog=isXLog, isYLog=isYLog, dpi=self.getDotsPerInch()) + isXLog=isXLog, + isYLog=isYLog, + dpi=self.getDotsPerInch(), + plotFrame=self._plotFrame, + ) for plotItem in self.getItemsFromBackToFront( condition=lambda i: i.isVisible() and i.isOverlay() == overlay): @@ -526,7 +535,7 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): color = item['color'] intensity = color[0] * 0.299 + color[1] * 0.587 + color[2] * 0.114 - bgColor = (1., 1., 1., 0.5) if intensity <= 0.5 else (0., 0., 0., 0.5) + bgColor = (1., 1., 1., 0.75) if intensity <= 0.5 else (0., 0., 0., 0.75) if xCoord is None or yCoord is None: if xCoord is None: # Horizontal line in data space pixelPos = self._plotFrame.dataToPixel( @@ -612,20 +621,15 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): # For now simple implementation: using a curve for each marker # Should pack all markers to a single set of points - markerCurve = glutils.GLPlotCurve2D( - numpy.array((pixelPos[0],), dtype=numpy.float64), - numpy.array((pixelPos[1],), dtype=numpy.float64), + marker = glutils.Points2D( + (pixelPos[0],), + (pixelPos[1],), marker=item['symbol'], - markerColor=item['color'], - markerSize=11) - - context = glutils.RenderContext( - matrix=self.matScreenProj, - isXLog=False, - isYLog=False, - dpi=self.getDotsPerInch()) - markerCurve.render(context) - markerCurve.discard() + color=item['color'], + size=11, + ) + context.matrix = self.matScreenProj + marker.render(context) else: _logger.error('Unsupported item: %s', str(item)) diff --git a/src/silx/gui/plot/backends/__init__.py b/src/silx/gui/plot/backends/__init__.py index 966d9df..d75a943 100644 --- a/src/silx/gui/plot/backends/__init__.py +++ b/src/silx/gui/plot/backends/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/backends/glutils/GLPlotCurve.py b/src/silx/gui/plot/backends/glutils/GLPlotCurve.py index e4667b4..65bb6f0 100644 --- a/src/silx/gui/plot/backends/glutils/GLPlotCurve.py +++ b/src/silx/gui/plot/backends/glutils/GLPlotCurve.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2021 European Synchrotron Radiation Facility +# Copyright (c) 2014-2022 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 @@ -26,8 +25,6 @@ This module provides classes to render 2D lines and scatter plots """ -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "03/04/2017" @@ -303,7 +300,7 @@ class GLLines2D(object): #version 120 uniform mat4 matrix; - uniform vec2 halfViewportSize; + uniform float distanceScale; attribute float xPos; attribute float yPos; attribute vec4 color; @@ -314,11 +311,7 @@ class GLLines2D(object): void main(void) { gl_Position = matrix * vec4(xPos, yPos, 0., 1.); - //Estimate distance in pixels - vec2 probe = vec2(matrix * vec4(1., 1., 0., 0.)) * - halfViewportSize; - float pixelPerDataEstimate = length(probe)/sqrt(2.); - vDist = distance * pixelPerDataEstimate; + vDist = distance * distanceScale; vColor = color; } """, @@ -427,10 +420,6 @@ class GLLines2D(object): program = self._DASH_PROGRAM program.use() - x, y, viewWidth, viewHeight = gl.glGetFloatv(gl.GL_VIEWPORT) - gl.glUniform2f(program.uniforms['halfViewportSize'], - 0.5 * viewWidth, 0.5 * viewHeight) - dashPeriod = self.dashPeriod * width if self.style == DOTTED: dash = (0.2 * dashPeriod, @@ -457,6 +446,13 @@ class GLLines2D(object): dash2ndColor = self.dash2ndColor gl.glUniform4f(program.uniforms['dash2ndColor'], *dash2ndColor) + viewWidth = gl.glGetFloatv(gl.GL_VIEWPORT)[2] + xNDCPerData = ( + numpy.dot(context.matrix, [1., 0., 0., 1.])[0] - + numpy.dot(context.matrix, [0., 0., 0., 1.])[0]) + xPixelPerData = 0.5 * viewWidth * xNDCPerData + gl.glUniform1f(program.uniforms['distanceScale'], xPixelPerData) + distAttrib = program.attributes['distance'] gl.glEnableVertexAttribArray(distAttrib) if isinstance(self.distVboData, VertexBufferAttrib): @@ -515,11 +511,12 @@ class GLLines2D(object): gl.glDisable(gl.GL_LINE_SMOOTH) -def distancesFromArrays(xData, yData): +def distancesFromArrays(xData, yData, ratio: float=1.): """Returns distances between each points :param numpy.ndarray xData: X coordinate of points :param numpy.ndarray yData: Y coordinate of points + :param ratio: Y/X pixel per data resolution ratio :rtype: numpy.ndarray """ # Split array into sub-shapes at not finite points @@ -534,11 +531,11 @@ def distancesFromArrays(xData, yData): if begin == end: # Empty shape continue elif end - begin == 1: # Single element - distances.append([0]) + distances.append(numpy.array([0], dtype=numpy.float32)) else: deltas = numpy.dstack(( numpy.ediff1d(xData[begin:end], to_begin=numpy.float32(0.)), - numpy.ediff1d(yData[begin:end], to_begin=numpy.float32(0.))))[0] + numpy.ediff1d(yData[begin:end] * ratio, to_begin=numpy.float32(0.))))[0] distances.append( numpy.cumsum(numpy.sqrt(numpy.sum(deltas ** 2, axis=1)))) return numpy.concatenate(distances) @@ -561,7 +558,7 @@ CARET_UP = "caretup" CARET_DOWN = "caretdown" -class _Points2D(object): +class Points2D(object): """Object rendering curve markers :param xVboData: X coordinates VBO @@ -809,8 +806,18 @@ class _Points2D(object): self.size = size self.offset = offset + if (xVboData is not None and + not isinstance(xVboData, VertexBufferAttrib)): + xVboData = numpy.array(xVboData, copy=False, dtype=numpy.float32) self.xVboData = xVboData + + if (yVboData is not None and + not isinstance(yVboData, VertexBufferAttrib)): + yVboData = numpy.array(yVboData, copy=False, dtype=numpy.float32) self.yVboData = yVboData + + if colorVboData is not None: + assert isinstance(colorVboData, VertexBufferAttrib) self.colorVboData = colorVboData self.useColorVboData = colorVboData is not None @@ -894,18 +901,33 @@ class _Points2D(object): gl.glDisableVertexAttribArray(cAttrib) gl.glVertexAttrib4f(cAttrib, *self.color) - xAttrib = program.attributes['xPos'] - gl.glEnableVertexAttribArray(xAttrib) - self.xVboData.setVertexAttrib(xAttrib) + xPosAttrib = program.attributes['xPos'] + gl.glEnableVertexAttribArray(xPosAttrib) + if isinstance(self.xVboData, VertexBufferAttrib): + self.xVboData.setVertexAttrib(xPosAttrib) + else: + gl.glVertexAttribPointer(xPosAttrib, + 1, + gl.GL_FLOAT, + False, + 0, + self.xVboData) + + yPosAttrib = program.attributes['yPos'] + gl.glEnableVertexAttribArray(yPosAttrib) + if isinstance(self.yVboData, VertexBufferAttrib): + self.yVboData.setVertexAttrib(yPosAttrib) + else: + gl.glVertexAttribPointer(yPosAttrib, + 1, + gl.GL_FLOAT, + False, + 0, + self.yVboData) - yAttrib = program.attributes['yPos'] - gl.glEnableVertexAttribArray(yAttrib) - self.yVboData.setVertexAttrib(yAttrib) gl.glDrawArrays(gl.GL_POINTS, 0, self.xVboData.size) - gl.glUseProgram(0) - # error bars ################################################################## @@ -959,9 +981,9 @@ class _ErrorBars(object): self._lines = GLLines2D( None, None, color=color, drawMode=gl.GL_LINES, offset=offset) - self._xErrPoints = _Points2D( + self._xErrPoints = Points2D( None, None, color=color, marker=V_LINE, offset=offset) - self._yErrPoints = _Points2D( + self._yErrPoints = Points2D( None, None, color=color, marker=H_LINE, offset=offset) def _buildVertices(self): @@ -1117,6 +1139,7 @@ class GLPlotCurve2D(GLPlotItem): baseline=None, isYLog=False): super().__init__() + self._ratio = None self.colorData = colorData # Compute x bounds @@ -1192,7 +1215,7 @@ class GLPlotCurve2D(GLPlotItem): self.lines.dashPeriod = lineDashPeriod self.lines.offset = self.offset - self.points = _Points2D() + self.points = Points2D() self.points.marker = marker self.points.color = markerColor self.points.size = markerSize @@ -1228,14 +1251,14 @@ class GLPlotCurve2D(GLPlotItem): def init(cls): """OpenGL context initialization""" GLLines2D.init() - _Points2D.init() + Points2D.init() def prepare(self): """Rendering preparation: build indices and bounding box vertices""" if self.xVboData is None: xAttrib, yAttrib, cAttrib, dAttrib = None, None, None, None if self.lineStyle in (DASHED, DASHDOT, DOTTED): - dists = distancesFromArrays(self.xData, self.yData) + dists = distancesFromArrays(self.xData, self.yData, self._ratio) if self.colorData is None: xAttrib, yAttrib, dAttrib = vertexBuffer( (self.xData, self.yData, dists)) @@ -1262,6 +1285,17 @@ class GLPlotCurve2D(GLPlotItem): :param RenderContext context: Rendering information """ + if self.lineStyle in (DASHED, DASHDOT, DOTTED): + visibleRanges = context.plotFrame.transformedDataRanges + xLimits = visibleRanges.x + yLimits = visibleRanges.y if self.yaxis == 'left' else visibleRanges.y2 + width, height = context.plotFrame.plotSize + ratio = (height * (xLimits[1] - xLimits[0])) / (width * (yLimits[1] - yLimits[0])) + if self._ratio is None or abs(1. - ratio/self._ratio) > 0.05: # Tolerate 5% difference + # Rebuild curve buffers to update distances + self._ratio = ratio + self.discard() + self.prepare() if self.fill is not None: self.fill.render(context) diff --git a/src/silx/gui/plot/backends/glutils/GLPlotFrame.py b/src/silx/gui/plot/backends/glutils/GLPlotFrame.py index 1fccb02..e5fabf2 100644 --- a/src/silx/gui/plot/backends/glutils/GLPlotFrame.py +++ b/src/silx/gui/plot/backends/glutils/GLPlotFrame.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2021 European Synchrotron Radiation Facility +# Copyright (c) 2014-2022 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 @@ -39,6 +38,8 @@ import datetime as dt import math import weakref import logging +import numbers +from typing import Optional, Union from collections import namedtuple import numpy @@ -358,8 +359,12 @@ class PlotAxis(object): yield ((xPixel, yPixel), dataPos, text) else: # Time series - dtMin = dt.datetime.fromtimestamp(dataMin, tz=self.timeZone) - dtMax = dt.datetime.fromtimestamp(dataMax, tz=self.timeZone) + try: + dtMin = dt.datetime.fromtimestamp(dataMin, tz=self.timeZone) + dtMax = dt.datetime.fromtimestamp(dataMax, tz=self.timeZone) + except ValueError: + _logger.warning("Data range cannot be displayed with time axis") + return # Range is out of bound of the datetime tickDateTimes, spacing, unit = calcTicksAdaptive( dtMin, dtMax, nbPixels, tickDensity) @@ -984,6 +989,26 @@ class GLPlotFrame2D(GLPlotFrame): return self._transformedDataY2ProjMat + @staticmethod + def __applyLog( + data: Union[float, numpy.ndarray], + isLog: bool + ) -> Optional[Union[float, numpy.ndarray]]: + """Apply log to data filtering out """ + if not isLog: + return data + + if isinstance(data, numbers.Real): + return None if data < FLOAT32_MINPOS else math.log10(data) + + isBelowMin = data < FLOAT32_MINPOS + if numpy.any(isBelowMin): + data = numpy.array(data, copy=True, dtype=numpy.float64) + data[isBelowMin] = numpy.nan + + with numpy.errstate(divide='ignore'): + return numpy.log10(data) + def dataToPixel(self, x, y, axis='left'): """Convert data coordinate to widget pixel coordinate. """ @@ -991,19 +1016,13 @@ class GLPlotFrame2D(GLPlotFrame): trBounds = self.transformedDataRanges - if self.xAxis.isLog: - if x < FLOAT32_MINPOS: - return None - xDataTr = math.log10(x) - else: - xDataTr = x + xDataTr = self.__applyLog(x, self.xAxis.isLog) + if xDataTr is None: + return None - if self.yAxis.isLog: - if y < FLOAT32_MINPOS: - return None - yDataTr = math.log10(y) - else: - yDataTr = y + yDataTr = self.__applyLog(y, self.yAxis.isLog) + if yDataTr is None: + return None # Non-orthogonal axes if self.baseVectors != self.DEFAULT_BASE_VECTORS: @@ -1015,20 +1034,23 @@ class GLPlotFrame2D(GLPlotFrame): plotWidth, plotHeight = self.plotSize - xPixel = int(self.margins.left + - plotWidth * (xDataTr - trBounds.x[0]) / - (trBounds.x[1] - trBounds.x[0])) + xPixel = (self.margins.left + + plotWidth * (xDataTr - trBounds.x[0]) / + (trBounds.x[1] - trBounds.x[0])) usedAxis = trBounds.y if axis == "left" else trBounds.y2 yOffset = (plotHeight * (yDataTr - usedAxis[0]) / (usedAxis[1] - usedAxis[0])) if self.isYAxisInverted: - yPixel = int(self.margins.top + yOffset) + yPixel = self.margins.top + yOffset else: - yPixel = int(self.size[1] - self.margins.bottom - yOffset) + yPixel = self.size[1] - self.margins.bottom - yOffset - return xPixel, yPixel + return ( + int(xPixel) if isinstance(xPixel, numbers.Real) else xPixel.astype(numpy.int64), + int(yPixel) if isinstance(yPixel, numbers.Real) else yPixel.astype(numpy.int64), + ) def pixelToData(self, x, y, axis="left"): """Convert pixel position to data coordinates. diff --git a/src/silx/gui/plot/backends/glutils/GLPlotImage.py b/src/silx/gui/plot/backends/glutils/GLPlotImage.py index 3ad94b9..8353911 100644 --- a/src/silx/gui/plot/backends/glutils/GLPlotImage.py +++ b/src/silx/gui/plot/backends/glutils/GLPlotImage.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2021 European Synchrotron Radiation Facility @@ -159,6 +158,7 @@ class GLPlotColormap(_GLPlotData2D): } uniform sampler2D data; + uniform float data_scale; uniform sampler2D cmap_texture; uniform int cmap_normalization; uniform float cmap_parameter; @@ -174,35 +174,35 @@ class GLPlotColormap(_GLPlotData2D): const float oneOverLog10 = 0.43429448190325176; void main(void) { - float data = texture2D(data, textureCoords()).r; - float value = data; + float raw_data = texture2D(data, textureCoords()).r * data_scale; + float value = 0.; if (cmap_normalization == 1) { /*Logarithm mapping*/ - if (value > 0.) { + if (raw_data > 0.) { value = clamp(cmap_oneOverRange * - (oneOverLog10 * log(value) - cmap_min), + (oneOverLog10 * log(raw_data) - cmap_min), 0., 1.); } else { value = 0.; } } else if (cmap_normalization == 2) { /*Square root mapping*/ - if (value >= 0.) { - value = clamp(cmap_oneOverRange * (sqrt(value) - cmap_min), + if (raw_data >= 0.) { + value = clamp(cmap_oneOverRange * (sqrt(raw_data) - cmap_min), 0., 1.); } else { value = 0.; } } else if (cmap_normalization == 3) { /*Gamma correction mapping*/ value = pow( - clamp(cmap_oneOverRange * (value - cmap_min), 0., 1.), + clamp(cmap_oneOverRange * (raw_data - cmap_min), 0., 1.), cmap_parameter); } else if (cmap_normalization == 4) { /* arcsinh mapping */ /* asinh = log(x + sqrt(x*x + 1) for compatibility with GLSL 1.20 */ - value = clamp(cmap_oneOverRange * (log(value + sqrt(value*value + 1.0)) - cmap_min), 0., 1.); + value = clamp(cmap_oneOverRange * (log(raw_data + sqrt(raw_data*raw_data + 1.0)) - cmap_min), 0., 1.); } else { /*Linear mapping and fallback*/ - value = clamp(cmap_oneOverRange * (value - cmap_min), 0., 1.); + value = clamp(cmap_oneOverRange * (raw_data - cmap_min), 0., 1.); } - if (isnan(data)) { + if (isnan(raw_data)) { gl_FragColor = nancolor; } else { gl_FragColor = texture2D(cmap_texture, vec2(value, 0.5)); @@ -355,9 +355,10 @@ class GLPlotColormap(_GLPlotData2D): if self.data.dtype in (numpy.uint16, numpy.uint8): # Using unsigned int as normalized integer in OpenGL - # So normalize range - maxInt = float(numpy.iinfo(self.data.dtype).max) - dataMin, dataMax = dataMin / maxInt, dataMax / maxInt + # So revert normalization in the shader + dataScale = float(numpy.iinfo(self.data.dtype).max) + else: + dataScale = 1. if self.normalization == 'log': dataMin = math.log10(dataMin) @@ -378,6 +379,7 @@ class GLPlotColormap(_GLPlotData2D): else: # Linear and fallback normID = 0 + gl.glUniform1f(prog.uniforms['data_scale'], dataScale) gl.glUniform1i(prog.uniforms['cmap_texture'], self._cmap_texture.texUnit) gl.glUniform1i(prog.uniforms['cmap_normalization'], normID) diff --git a/src/silx/gui/plot/backends/glutils/GLPlotItem.py b/src/silx/gui/plot/backends/glutils/GLPlotItem.py index ae13091..58f5f41 100644 --- a/src/silx/gui/plot/backends/glutils/GLPlotItem.py +++ b/src/silx/gui/plot/backends/glutils/GLPlotItem.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2020-2021 European Synchrotron Radiation Facility +# Copyright (c) 2020-2022 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 @@ -40,13 +39,14 @@ class RenderContext: :param float dpi: Number of device pixels per inch """ - def __init__(self, matrix=None, isXLog=False, isYLog=False, dpi=96.): + def __init__(self, matrix=None, isXLog=False, isYLog=False, dpi=96., plotFrame=None): self.matrix = matrix """Current transformation matrix""" self.__isXLog = isXLog self.__isYLog = isYLog self.__dpi = dpi + self.__plotFrame = plotFrame @property def isXLog(self): @@ -63,6 +63,11 @@ class RenderContext: """Number of device pixels per inch""" return self.__dpi + @property + def plotFrame(self): + """Current PlotFrame""" + return self.__plotFrame + class GLPlotItem: """Base class for primitives used in the PlotWidget OpenGL backend""" diff --git a/src/silx/gui/plot/backends/glutils/GLPlotTriangles.py b/src/silx/gui/plot/backends/glutils/GLPlotTriangles.py index fbe9e02..a67afd9 100644 --- a/src/silx/gui/plot/backends/glutils/GLPlotTriangles.py +++ b/src/silx/gui/plot/backends/glutils/GLPlotTriangles.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/backends/glutils/GLSupport.py b/src/silx/gui/plot/backends/glutils/GLSupport.py index da6dffa..f5357e2 100644 --- a/src/silx/gui/plot/backends/glutils/GLSupport.py +++ b/src/silx/gui/plot/backends/glutils/GLSupport.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/backends/glutils/GLText.py b/src/silx/gui/plot/backends/glutils/GLText.py index d6ae6fa..4862bff 100644 --- a/src/silx/gui/plot/backends/glutils/GLText.py +++ b/src/silx/gui/plot/backends/glutils/GLText.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility @@ -241,7 +240,7 @@ class Text2D(object): return vertices def render(self, matrix): - if not self.text: + if not self.text.strip(): return prog = self._program diff --git a/src/silx/gui/plot/backends/glutils/GLTexture.py b/src/silx/gui/plot/backends/glutils/GLTexture.py index 37fbdd0..caca111 100644 --- a/src/silx/gui/plot/backends/glutils/GLTexture.py +++ b/src/silx/gui/plot/backends/glutils/GLTexture.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/backends/glutils/PlotImageFile.py b/src/silx/gui/plot/backends/glutils/PlotImageFile.py index 5fb6853..75ee50b 100644 --- a/src/silx/gui/plot/backends/glutils/PlotImageFile.py +++ b/src/silx/gui/plot/backends/glutils/PlotImageFile.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/backends/glutils/__init__.py b/src/silx/gui/plot/backends/glutils/__init__.py index f87d7c1..bc15b78 100644 --- a/src/silx/gui/plot/backends/glutils/__init__.py +++ b/src/silx/gui/plot/backends/glutils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2014-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/__init__.py b/src/silx/gui/plot/items/__init__.py index 0fe29c2..6e26c64 100644 --- a/src/silx/gui/plot/items/__init__.py +++ b/src/silx/gui/plot/items/__init__.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -42,7 +41,7 @@ from .curve import Curve, CurveStyle # noqa from .histogram import Histogram # noqa from .image import ImageBase, ImageData, ImageDataBase, ImageRgba, ImageStack, MaskImageData # noqa from .image_aggregated import ImageDataAggregated # noqa -from .shape import Shape, BoundingRect, XAxisExtent, YAxisExtent # noqa +from .shape import Line, Shape, BoundingRect, XAxisExtent, YAxisExtent # noqa from .scatter import Scatter # noqa from .marker import MarkerBase, Marker, XMarker, YMarker # noqa from .axis import Axis, XAxis, YAxis, YRightAxis diff --git a/src/silx/gui/plot/items/_arc_roi.py b/src/silx/gui/plot/items/_arc_roi.py index 23416ec..40711b7 100644 --- a/src/silx/gui/plot/items/_arc_roi.py +++ b/src/silx/gui/plot/items/_arc_roi.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2018-2021 European Synchrotron Radiation Facility +# Copyright (c) 2018-2022 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 @@ -635,7 +634,9 @@ class ArcROI(HandleBasedROI, items.LineMixIn, InteractionModeMixIn): innerRadius = geometry.radius - geometry.weight * 0.5 outerRadius = geometry.radius + geometry.weight * 0.5 - delta = 0.1 if geometry.endAngle >= geometry.startAngle else -0.1 + sign = numpy.sign(geometry.endAngle - geometry.startAngle) + delta = min(0.1, abs(geometry.startAngle - geometry.endAngle) / 100) * sign + if geometry.startAngle == geometry.endAngle: # Degenerated, it's a line (single radius) angle = geometry.startAngle @@ -654,7 +655,6 @@ class ArcROI(HandleBasedROI, items.LineMixIn, InteractionModeMixIn): points = [] points.append(geometry.center) points.append(geometry.startPoint) - delta = 0.1 if geometry.endAngle >= geometry.startAngle else -0.1 for angle in angles: direction = numpy.array([numpy.cos(angle), numpy.sin(angle)]) points.append(geometry.center + direction * outerRadius) diff --git a/src/silx/gui/plot/items/_band_roi.py b/src/silx/gui/plot/items/_band_roi.py new file mode 100644 index 0000000..a60a177 --- /dev/null +++ b/src/silx/gui/plot/items/_band_roi.py @@ -0,0 +1,370 @@ +# /*########################################################################## +# +# Copyright (c) 2022 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. +# +# ###########################################################################*/ +"""Rectangular ROI that can be rotated""" + +import functools +import logging +from typing import Iterable, List, NamedTuple, Optional, Sequence, Tuple +import numpy + +from ... import qt, utils +from .. import items +from ...colors import rgba +from silx.image.shapes import Polygon +from ....utils.proxy import docstring +from ._roi_base import _RegionOfInterestBase +from ._roi_base import HandleBasedROI +from ._roi_base import InteractionModeMixIn +from ._roi_base import RoiInteractionMode + + +logger = logging.getLogger(__name__) + + +class Point(NamedTuple): + x: float + y: float + + +class BandGeometry(NamedTuple): + begin: Point + end: Point + width: float + + @staticmethod + def create( + begin: Sequence[float] = (0.0, 0.0), + end: Sequence[float] = (0.0, 0.0), + width: Optional[float] = None, + ): + begin = Point(float(begin[0]), float(begin[1])) + end = Point(float(end[0]), float(end[1])) + if width is None: + width = 0.1 * numpy.linalg.norm(numpy.array(end) - begin) + return BandGeometry(begin, end, max(0.0, float(width))) + + @property + @functools.lru_cache() + def normal(self) -> Point: + vector = numpy.array(self.end) - self.begin + length = numpy.linalg.norm(vector) + if length == 0: + return Point(0.0, 0.0) + return Point(-vector[1] / length, vector[0] / length) + + @property + @functools.lru_cache() + def center(self) -> Point: + return Point(*(0.5 * (numpy.array(self.begin) + self.end))) + + @property + @functools.lru_cache() + def corners(self) -> Tuple[Point, Point, Point, Point]: + """Returns a 4-uple of (x,y) position in float""" + offset = 0.5 * self.width * numpy.array(self.normal) + return tuple( + map( + lambda p: Point(*p), + ( + self.begin - offset, + self.begin + offset, + self.end + offset, + self.end - offset, + ), + ) + ) + + @property + @functools.lru_cache() + def slope(self) -> float: + """Slope of the line (begin, end), infinity for a vertical line""" + if self.begin.x == self.end.x: + return float('inf') + return (self.end.y - self.begin.y) / (self.end.x - self.begin.x) + + @property + @functools.lru_cache() + def intercept(self) -> float: + """Intercept of the line (begin, end) or value of x for vertical line""" + if self.begin.x == self.end.x: + return self.begin.x + return self.begin.y - self.slope * self.begin.x + + @property + @functools.lru_cache() + def edgesIntercept(self) -> Tuple[float, float]: + """Intercepts of lines describing band edges""" + offset = 0.5 * self.width * numpy.array(self.normal) + if self.begin.x == self.end.x: + return self.begin.x - offset[0], self.begin.x + offset[0] + return ( + self.begin.y - offset[1] - self.slope * (self.begin.x - offset[0]), + self.begin.y + offset[1] - self.slope * (self.begin.x + offset[0]), + ) + + def contains(self, position: Sequence[float]) -> bool: + return Polygon(self.corners).is_inside(*position) + + +class BandROI(HandleBasedROI, items.LineMixIn, InteractionModeMixIn): + """A ROI identifying a line in a 2D plot. + + This ROI provides 1 anchor for each boundary of the line, plus an center + in the center to translate the full ROI. + """ + + ICON = "add-shape-rotated-rectangle" + NAME = "band ROI" + SHORT_NAME = "band" + """Metadata for this kind of ROI""" + + _plotShape = "line" + """Plot shape which is used for the first interaction""" + + BoundedMode = RoiInteractionMode("Bounded", "Band is bounded on both sides") + """Interaction mode for a rectangular band ROI""" + + UnboundedMode = RoiInteractionMode("Unbounded", "Band is unbounded on both sides") + """Interaction mode for unlimited band ROI """ + + def __init__(self, parent=None): + HandleBasedROI.__init__(self, parent=parent) + items.LineMixIn.__init__(self) + self.__availableInteractionModes = set((self.BoundedMode, self.UnboundedMode)) + InteractionModeMixIn.__init__(self) + + self.__handleBegin = self.addHandle() + self.__handleEnd = self.addHandle() + self.__handleCenter = self.addTranslateHandle() + self.__handleLabel = self.addLabelHandle() + self.__handleWidthUp = self.addHandle() + self.__handleWidthUp._setConstraint(self.__handleWidthUpConstraint) + self.__handleWidthUp.setSymbol("d") + self.__handleWidthDown = self.addHandle() + self.__handleWidthDown._setConstraint(self.__handleWidthDownConstraint) + self.__handleWidthDown.setSymbol("d") + + self.__geometry = BandGeometry.create() + + self.__lineUp = items.Line() + self.__lineUp.setVisible(False) + self.__lineMiddle = items.Line() + self.__lineMiddle.setLineWidth(1) + self.__lineMiddle.setVisible(False) + self.__lineDown = items.Line() + self.__lineDown.setVisible(False) + + self.__shape = items.Shape("polygon") + self.__shape.setPoints(self.__geometry.corners) + self.__shape.setFill(False) + + for item in (self.__lineUp, self.__lineMiddle, self.__lineDown, self.__shape): + item.setColor(rgba(self.getColor())) + item.setOverlay(True) + item.setLineStyle(self.getLineStyle()) + if item != self.__lineMiddle: + item.setLineWidth(self.getLineWidth()) + self.addItem(item) + + self._initInteractionMode(self.BoundedMode) + self._interactiveModeUpdated(self.BoundedMode) + + def availableInteractionModes(self) -> List[RoiInteractionMode]: + """Returns the list of available interaction modes""" + return list(self.__availableInteractionModes) + + def setAvailableInteractionModes(self, modes: Iterable[RoiInteractionMode]) -> None: + """Allows to restrict interaction modes of the ROI. + + :param modes: Subset of BandROI interaction modes: + :attr:`BoundedMode` and :attr:`UnboundedMode`. + """ + modes = set(modes) + if not modes <= set((self.BoundedMode, self.UnboundedMode)): + raise ValueError("Unsupported interaction modes") + self.__availableInteractionModes = set(modes) + if self.getInteractionMode() not in self.__availableInteractionModes: + self.setInteractionMode(self.availableInteractionModes()[0]) + + def _interactiveModeUpdated(self, modeId: RoiInteractionMode): + """Set the interaction mode.""" + if modeId is self.BoundedMode: + self.__lineDown.setVisible(False) + self.__lineMiddle.setVisible(False) + self.__lineUp.setVisible(False) + self.__shape.setVisible(True) + elif modeId is self.UnboundedMode: + self.__lineDown.setVisible(True) + self.__lineMiddle.setVisible(True) + self.__lineUp.setVisible(True) + self.__shape.setVisible(False) + else: + raise RuntimeError("Unsupported interactive mode") + + def _updated(self, event=None, checkVisibility=True): + if event == items.ItemChangedType.VISIBLE: + if self.isVisible(): + self._interactiveModeUpdated(self.getInteractionMode()) + else: + self.__lineDown.setVisible(False) + self.__lineMiddle.setVisible(False) + self.__lineUp.setVisible(False) + self.__shape.setVisible(False) + super()._updated(event, checkVisibility) + + def _updatedStyle(self, event, style): + super()._updatedStyle(event, style) + for item in (self.__lineUp, self.__lineMiddle, self.__lineDown, self.__shape): + item.setColor(style.getColor()) + item.setLineStyle(style.getLineStyle()) + if item != self.__lineMiddle: + item.setLineWidth(style.getLineWidth()) + + def setFirstShapePoints(self, points): + assert len(points) == 2 + self.setGeometry(*points) + + def _updateText(self, text): + self.__handleLabel.setText(text) + + def getGeometry(self) -> BandGeometry: + """Returns the geometric description of the ROI""" + return self.__geometry + + def setGeometry( + self, + begin: Sequence[float], + end: Sequence[float], + width: Optional[float] = None, + ): + """Set the geometry of the ROI + + :param begin: Starting point as (x, y) + :paran end: Closing point as (x, y) + :param width: Width of the ROI + """ + geometry = BandGeometry.create(begin, end, width) + if self.__geometry == geometry: + return + + self.__geometry = geometry + + with utils.blockSignals(self.__handleBegin): + self.__handleBegin.setPosition(*geometry.begin) + with utils.blockSignals(self.__handleEnd): + self.__handleEnd.setPosition(*geometry.end) + with utils.blockSignals(self.__handleCenter): + self.__handleCenter.setPosition(*geometry.center) + with utils.blockSignals(self.__handleLabel): + lowerCorner = geometry.corners[numpy.array(geometry.corners)[:, 1].argmin()] + self.__handleLabel.setPosition(*lowerCorner) + + delta = 0.5 * geometry.width * numpy.array(geometry.normal) + with utils.blockSignals(self.__handleWidthUp): + self.__handleWidthUp.setPosition(*(geometry.center + delta)) + with utils.blockSignals(self.__handleWidthDown): + self.__handleWidthDown.setPosition(*(geometry.center - delta)) + + self.__lineDown.setSlope(geometry.slope) + self.__lineDown.setIntercept(geometry.edgesIntercept[0]) + self.__lineMiddle.setSlope(geometry.slope) + self.__lineMiddle.setIntercept(geometry.intercept) + self.__lineUp.setSlope(geometry.slope) + self.__lineUp.setIntercept(geometry.edgesIntercept[1]) + self.__shape.setPoints(geometry.corners) + self.sigRegionChanged.emit() + + def __updateGeometry( + self, + begin: Optional[Sequence[float]] = None, + end: Optional[Sequence[float]] = None, + width: Optional[float] = None, + ): + geometry = self.getGeometry() + self.setGeometry( + geometry.begin if begin is None else begin, + geometry.end if end is None else end, + geometry.width if width is None else width, + ) + + @staticmethod + def __snap(point: Tuple[float, float], fixed: Tuple[float, float]) -> Tuple[float, float]: + """Snap point so that vector [point, fixed] snap to direction 0, 45 or 90 degrees + + :return: the snapped point position. + """ + vector = point[0] - fixed[0], point[1] - fixed[1] + angle = numpy.arctan2(vector[1], vector[0]) + snapAngle = numpy.pi/4 * numpy.round(angle / (numpy.pi/4)) + length = numpy.linalg.norm(vector) + return ( + fixed[0] + length * numpy.cos(snapAngle), + fixed[1] + length * numpy.sin(snapAngle) + ) + + def handleDragUpdated(self, handle, origin, previous, current): + geometry = self.getGeometry() + if handle is self.__handleBegin: + if qt.QApplication.keyboardModifiers() & qt.Qt.ShiftModifier: + self.__updateGeometry(begin=self.__snap(current, geometry.end)) + return + self.__updateGeometry(begin=current) + return + if handle is self.__handleEnd: + if qt.QApplication.keyboardModifiers() & qt.Qt.ShiftModifier: + self.__updateGeometry(end=self.__snap(current, geometry.begin)) + return + self.__updateGeometry(end=current) + return + if handle is self.__handleCenter: + delta = current - previous + self.__updateGeometry(geometry.begin + delta, geometry.end + delta) + return + if handle in (self.__handleWidthUp, self.__handleWidthDown): + offset = numpy.dot(geometry.normal, current - previous) + if handle is self.__handleWidthDown: + offset *= -1 + self.__updateGeometry( + geometry.begin, + geometry.end, + geometry.width + 2 * offset, + ) + + def __handleWidthUpConstraint(self, x: float, y: float) -> Tuple[float, float]: + geometry = self.getGeometry() + offset = max(0, numpy.dot(geometry.normal, numpy.array((x, y)) - geometry.center)) + return tuple(geometry.center + offset * numpy.array(geometry.normal)) + + def __handleWidthDownConstraint(self, x: float, y: float) -> Tuple[float, float]: + geometry = self.getGeometry() + offset = max(0, -numpy.dot(geometry.normal, numpy.array((x, y)) - geometry.center)) + return tuple(geometry.center - offset * numpy.array(geometry.normal)) + + @docstring(_RegionOfInterestBase) + def contains(self, position): + return self.getGeometry().contains(position) + + def __str__(self): + begin, end, width = self.getGeometry() + return f"{self.__class__.__name__}(begin=({begin[0]:g}, {begin[1]:g}), end=({end[0]:g}, {end[1]:g}), width={width:g})" diff --git a/src/silx/gui/plot/items/_pick.py b/src/silx/gui/plot/items/_pick.py index 8c8e781..631a30a 100644 --- a/src/silx/gui/plot/items/_pick.py +++ b/src/silx/gui/plot/items/_pick.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/_roi_base.py b/src/silx/gui/plot/items/_roi_base.py index 3eb6cf4..765a538 100644 --- a/src/silx/gui/plot/items/_roi_base.py +++ b/src/silx/gui/plot/items/_roi_base.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/axis.py b/src/silx/gui/plot/items/axis.py index c73323e..fa3f6d7 100644 --- a/src/silx/gui/plot/items/axis.py +++ b/src/silx/gui/plot/items/axis.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -480,26 +479,10 @@ class YRightAxis(Axis): """ Axis.__init__(self, plot) self.__mainAxis = mainAxis - - @property - def sigInvertedChanged(self): - """Signal emitted when axis orientation has changed""" - return self.__mainAxis.sigInvertedChanged - - @property - def sigScaleChanged(self): - """Signal emitted when axis scale has changed""" - return self.__mainAxis.sigScaleChanged - - @property - def _sigLogarithmicChanged(self): - """Signal emitted when axis scale has changed to or from logarithmic""" - return self.__mainAxis._sigLogarithmicChanged - - @property - def sigAutoScaleChanged(self): - """Signal emitted when axis autoscale has changed""" - return self.__mainAxis.sigAutoScaleChanged + self.__mainAxis.sigInvertedChanged.connect(self.sigInvertedChanged.emit) + self.__mainAxis.sigScaleChanged.connect(self.sigScaleChanged.emit) + self.__mainAxis._sigLogarithmicChanged.connect(self._sigLogarithmicChanged.emit) + self.__mainAxis.sigAutoScaleChanged.connect(self.sigAutoScaleChanged.emit) def _internalSetCurrentLabel(self, label): self._getBackend().setGraphYLabel(label, axis='right') diff --git a/src/silx/gui/plot/items/complex.py b/src/silx/gui/plot/items/complex.py index abb64ad..82d821f 100644 --- a/src/silx/gui/plot/items/complex.py +++ b/src/silx/gui/plot/items/complex.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides the :class:`ImageComplexData` of the :class:`Plot`. """ -from __future__ import absolute_import - __authors__ = ["Vincent Favre-Nicolin", "T. Vincent"] __license__ = "MIT" __date__ = "14/06/2018" diff --git a/src/silx/gui/plot/items/core.py b/src/silx/gui/plot/items/core.py index fa3b8cf..074c168 100644 --- a/src/silx/gui/plot/items/core.py +++ b/src/silx/gui/plot/items/core.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -37,8 +36,7 @@ except ImportError: # Python2 support from copy import deepcopy import logging import enum -from typing import Optional, Tuple -import warnings +from typing import Optional, Tuple, Union import weakref import numpy @@ -244,6 +242,8 @@ class Item(qt.QObject): # When visibility has changed, always mark as dirty self._updated(ItemChangedType.VISIBLE, checkVisibility=False) + if visible: + self._visibleBoundsChanged() def isOverlay(self): """Return true if item is drawn as an overlay. @@ -492,7 +492,8 @@ class DataItem(Item): :param bool checkVisibility: """ if not checkVisibility or self.isVisible(): - self._visibleBoundsChanged() + if self.isVisible(): + self._visibleBoundsChanged() # TODO hackish data range implementation plot = self.getPlot() @@ -1479,6 +1480,31 @@ class PointsBase(DataItem, SymbolMixIn, AlphaMixIn): return x, y, xerror, yerror + @staticmethod + def __minMaxDataWithError( + data: numpy.ndarray, + error: Optional[Union[float, numpy.ndarray]], + positiveOnly: bool + ) -> Tuple[float]: + if error is None: + min_, max_ = min_max(data, finite=True) + return min_, max_ + + # float, 1D or 2D array + dataMinusError = data - numpy.atleast_2d(error)[0] + dataMinusError = dataMinusError[numpy.isfinite(dataMinusError)] + if positiveOnly: + dataMinusError = dataMinusError[dataMinusError > 0] + min_ = numpy.nan if dataMinusError.size == 0 else numpy.min(dataMinusError) + + dataPlusError = data + numpy.atleast_2d(error)[-1] + dataPlusError = dataPlusError[numpy.isfinite(dataPlusError)] + if positiveOnly: + dataPlusError = dataPlusError[dataPlusError > 0] + max_ = numpy.nan if dataPlusError.size == 0 else numpy.max(dataPlusError) + + return min_, max_ + def _getBounds(self): if self.getXData(copy=False).size == 0: # Empty data return None @@ -1491,7 +1517,6 @@ class PointsBase(DataItem, SymbolMixIn, AlphaMixIn): xPositive = False yPositive = False - # TODO bounds do not take error bars into account if (xPositive, yPositive) not in self._boundsCache: # use the getData class method because instance method can be # overloaded to return additional arrays @@ -1500,12 +1525,13 @@ class PointsBase(DataItem, SymbolMixIn, AlphaMixIn): # hack to avoid duplicating caching mechanism in Scatter # (happens when cached data is used, caching done using # Scatter._logFilterData) - x, y, _xerror, _yerror = data[0], data[1], data[3], data[4] + x, y, xerror, yerror = data[0], data[1], data[3], data[4] else: - x, y, _xerror, _yerror = data + x, y, xerror, yerror = data + + xmin, xmax = self.__minMaxDataWithError(x, xerror, xPositive) + ymin, ymax = self.__minMaxDataWithError(y, yerror, yPositive) - xmin, xmax = min_max(x, finite=True) - ymin, ymax = min_max(y, finite=True) self._boundsCache[(xPositive, yPositive)] = tuple([ (bound if bound is not None else numpy.nan) for bound in (xmin, xmax, ymin, ymax)]) @@ -1600,8 +1626,8 @@ class PointsBase(DataItem, SymbolMixIn, AlphaMixIn): :type xerror: A float, or a numpy.ndarray of float32. If it is an array, it can either be a 1D array of same length as the data or a 2D array with 2 rows - of same length as the data: row 0 for positive errors, - row 1 for negative errors. + of same length as the data: row 0 for lower errors, + row 1 for upper errors. :param yerror: Values with the uncertainties on the y values. :type yerror: A float, or a numpy.ndarray of float32. See xerror. :param bool copy: True make a copy of the data (default), diff --git a/src/silx/gui/plot/items/curve.py b/src/silx/gui/plot/items/curve.py index 7cbe26e..93e4719 100644 --- a/src/silx/gui/plot/items/curve.py +++ b/src/silx/gui/plot/items/curve.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/histogram.py b/src/silx/gui/plot/items/histogram.py index 16bbefa..007f0c7 100644 --- a/src/silx/gui/plot/items/histogram.py +++ b/src/silx/gui/plot/items/histogram.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/image.py b/src/silx/gui/plot/items/image.py index 5cc719b..eaee05a 100644 --- a/src/silx/gui/plot/items/image.py +++ b/src/silx/gui/plot/items/image.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/image_aggregated.py b/src/silx/gui/plot/items/image_aggregated.py index 75fdd59..ffd41b2 100644 --- a/src/silx/gui/plot/items/image_aggregated.py +++ b/src/silx/gui/plot/items/image_aggregated.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/marker.py b/src/silx/gui/plot/items/marker.py index 50d070c..7596eb0 100755 --- a/src/silx/gui/plot/items/marker.py +++ b/src/silx/gui/plot/items/marker.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/items/roi.py b/src/silx/gui/plot/items/roi.py index 38a1424..559e7e0 100644 --- a/src/silx/gui/plot/items/roi.py +++ b/src/silx/gui/plot/items/roi.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2018-2020 European Synchrotron Radiation Facility +# Copyright (c) 2018-2022 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 @@ -50,6 +49,7 @@ from ._roi_base import _RegionOfInterestBase from ._roi_base import RegionOfInterest from ._roi_base import HandleBasedROI from ._arc_roi import ArcROI # noqa +from ._band_roi import BandROI # noqa from ._roi_base import InteractionModeMixIn # noqa from ._roi_base import RoiInteractionMode # noqa diff --git a/src/silx/gui/plot/items/scatter.py b/src/silx/gui/plot/items/scatter.py index fdc66f7..96fb311 100644 --- a/src/silx/gui/plot/items/scatter.py +++ b/src/silx/gui/plot/items/scatter.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -25,9 +24,6 @@ """This module provides the :class:`Scatter` item of the :class:`Plot`. """ -from __future__ import division - - __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "29/03/2017" @@ -950,8 +946,8 @@ class Scatter(PointsBase, ColormapMixIn, ScatterVisualizationMixIn): :type xerror: A float, or a numpy.ndarray of float32. If it is an array, it can either be a 1D array of same length as the data or a 2D array with 2 rows - of same length as the data: row 0 for positive errors, - row 1 for negative errors. + of same length as the data: row 0 for lower errors, + row 1 for upper errors. :param yerror: Values with the uncertainties on the y values :type yerror: A float, or a numpy.ndarray of float32. See xerror. :param alpha: Values with the transparency (between 0 and 1) diff --git a/src/silx/gui/plot/items/shape.py b/src/silx/gui/plot/items/shape.py index 00ac5f5..dc35864 100644 --- a/src/silx/gui/plot/items/shape.py +++ b/src/silx/gui/plot/items/shape.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -35,17 +34,78 @@ import logging import numpy from ... import colors +from ..utils.intersections import lines_intersection from .core import ( Item, DataItem, - ColorMixIn, FillMixIn, ItemChangedType, LineMixIn, YAxisMixIn) + AlphaMixIn, ColorMixIn, FillMixIn, ItemChangedType, ItemMixInBase, LineMixIn, YAxisMixIn) _logger = logging.getLogger(__name__) +class _OverlayItem(Item): + """Item with settable overlay""" + + def __init__(self): + self.__overlay = False + Item.__init__(self) + + def isOverlay(self) -> bool: + """Return true if shape is drawn as an overlay""" + return self.__overlay + + def setOverlay(self, overlay: bool): + """Set the overlay state of the shape + + :param overlay: True to make it an overlay + """ + overlay = bool(overlay) + if overlay != self.__overlay: + self.__overlay = overlay + self._updated(ItemChangedType.OVERLAY) + + +class _TwoColorsLineMixIn(LineMixIn): + """Mix-in class for items with a background color for dashes""" + + def __init__(self): + LineMixIn.__init__(self) + self.__backgroundColor = None + + def getLineBgColor(self): + """Returns the RGBA background color of dash line + + :rtype: 4-tuple of float in [0, 1] or array of colors + """ + return self.__backgroundColor + + def setLineBgColor(self, color, copy: bool=True): + """Set dash line background color + + :param color: color(s) to be used + :type color: str ("#RRGGBB") or (npoints, 4) unsigned byte array or + one of the predefined color names defined in colors.py + :param copy: True (Default) to get a copy, + False to use internal representation (do not modify!) + """ + if color is not None: + if isinstance(color, str): + color = colors.rgba(color) + else: + color = numpy.array(color, copy=copy) + # TODO more checks + improve color array support + if color.ndim == 1: # Single RGBA color + color = colors.rgba(color) + else: # Array of colors + assert color.ndim == 2 + + self.__backgroundColor = color + self._updated(ItemChangedType.LINE_BG_COLOR) + + # TODO probably make one class for each kind of shape # TODO check fill:polygon/polyline + fill = duplicated -class Shape(Item, ColorMixIn, FillMixIn, LineMixIn): +class Shape(_OverlayItem, ColorMixIn, FillMixIn, _TwoColorsLineMixIn): """Description of a shape item :param str type_: The type of shape in: @@ -53,16 +113,13 @@ class Shape(Item, ColorMixIn, FillMixIn, LineMixIn): """ def __init__(self, type_): - Item.__init__(self) + _OverlayItem.__init__(self) ColorMixIn.__init__(self) FillMixIn.__init__(self) - LineMixIn.__init__(self) - self._overlay = False + _TwoColorsLineMixIn.__init__(self) assert type_ in ('hline', 'polygon', 'rectangle', 'vline', 'polylines') self._type = type_ self._points = () - self._lineBgColor = None - self._handle = None def _addBackendRenderer(self, backend): @@ -79,23 +136,6 @@ class Shape(Item, ColorMixIn, FillMixIn, LineMixIn): linewidth=self.getLineWidth(), linebgcolor=self.getLineBgColor()) - def isOverlay(self): - """Return true if shape is drawn as an overlay - - :rtype: bool - """ - return self._overlay - - def setOverlay(self, overlay): - """Set the overlay state of the shape - - :param bool overlay: True to make it an overlay - """ - overlay = bool(overlay) - if overlay != self._overlay: - self._overlay = overlay - self._updated(ItemChangedType.OVERLAY) - def getType(self): """Returns the type of shape to draw. @@ -126,34 +166,6 @@ class Shape(Item, ColorMixIn, FillMixIn, LineMixIn): self._points = numpy.array(points, copy=copy) self._updated(ItemChangedType.DATA) - def getLineBgColor(self): - """Returns the RGBA color of the item - :rtype: 4-tuple of float in [0, 1] or array of colors - """ - return self._lineBgColor - - def setLineBgColor(self, color, copy=True): - """Set item color - :param color: color(s) to be used - :type color: str ("#RRGGBB") or (npoints, 4) unsigned byte array or - one of the predefined color names defined in colors.py - :param bool copy: True (Default) to get a copy, - False to use internal representation (do not modify!) - """ - if color is not None: - if isinstance(color, str): - color = colors.rgba(color) - else: - color = numpy.array(color, copy=copy) - # TODO more checks + improve color array support - if color.ndim == 1: # Single RGBA color - color = colors.rgba(color) - else: # Array of colors - assert color.ndim == 2 - - self._lineBgColor = color - self._updated(ItemChangedType.LINE_BG_COLOR) - class BoundingRect(DataItem, YAxisMixIn): """An invisible shape which enforce the plot view to display the defined @@ -285,3 +297,108 @@ class YAxisExtent(_BaseExtent, YAxisMixIn): def __init__(self): _BaseExtent.__init__(self, axis='y') YAxisMixIn.__init__(self) + + +class Line(_OverlayItem, AlphaMixIn, ColorMixIn, _TwoColorsLineMixIn): + """Description of a infinite line item as y = slope * x + interecpt + + Warning: If slope is not finite, then the line is x = intercept. + """ + + def __init__(self, slope: float=0, intercept: float=0): + assert numpy.isfinite(intercept) + + _OverlayItem.__init__(self) + AlphaMixIn.__init__(self) + ColorMixIn.__init__(self) + _TwoColorsLineMixIn.__init__(self) + self.__slope = float(slope) + self.__intercept = float(intercept) + self.__coordinates = None + self._setVisibleBoundsTracking(True) + + def __updatePoints(self): + if not self.isVisible(): + return + + plot = self.getPlot() + if plot is None or not plot.isVisible(): + return + + xmin, xmax = plot.getXAxis().getLimits() + ymin, ymax = plot.getYAxis().getLimits() + + slope = self.getSlope() + intercept = self.getIntercept() + + if not numpy.isfinite(slope): + if not xmin <= intercept <= xmax: + coordinates = None + else: + coordinates = (intercept, intercept), (ymin, ymax) + else: + ycoords = slope * xmin + intercept, slope * xmax + intercept + + if min(ycoords) < ymax and max(ycoords) > ymin: + coordinates = (xmin, xmax), ycoords + else: + coordinates = None + + if coordinates != self.__coordinates: + self.__coordinates = coordinates + self._updated() + + def _visibleBoundsChanged(self, *args) -> None: + """Override method to benefit from bounds tracking""" + self.__updatePoints() + return super()._visibleBoundsChanged(*args) + + def setSlope(self, slope: float): + slope = float(slope) + if slope != self.__slope: + self.__slope = slope + self.__updatePoints() + self._updated(ItemChangedType.DATA) + + def getSlope(self) -> float: + return self.__slope + + def setIntercept(self, intercept: float): + intercept = float(intercept) + assert numpy.isfinite(intercept) + if intercept != self.__intercept: + self.__intercept = intercept + self.__updatePoints() + self._updated(ItemChangedType.DATA) + + def getIntercept(self) -> float: + return self.__intercept + + def setSlopeInterceptFromPoints(self, point0, point1): + """Set slope and intercept from 2 (x, y) points""" + x0, y0 = point0 + x1, y1 = point1 + if x0 == x1: # Special case: vertical line + self.setSlope(float("inf")) + self.setIntercept(x0) + return + + slope = (y1 - y0) / (x1 - x0) + self.setSlope(slope) + self.setIntercept(y0 - x0 * slope) + + def _addBackendRenderer(self, backend): + """Update backend renderer""" + if self.__coordinates is None: + return None + + return backend.addShape( + *self.__coordinates, + shape='polylines', + color=self.getColor(), + fill=False, + overlay=self.isOverlay(), + linestyle=self.getLineStyle(), + linewidth=self.getLineWidth(), + linebgcolor=self.getLineBgColor(), + ) diff --git a/src/silx/gui/plot/matplotlib/Colormap.py b/src/silx/gui/plot/matplotlib/Colormap.py index dc432b2..1131df8 100644 --- a/src/silx/gui/plot/matplotlib/Colormap.py +++ b/src/silx/gui/plot/matplotlib/Colormap.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017-2020 European Synchrotron Radiation Facility # diff --git a/src/silx/gui/plot/matplotlib/__init__.py b/src/silx/gui/plot/matplotlib/__init__.py index e787240..155ffd4 100644 --- a/src/silx/gui/plot/matplotlib/__init__.py +++ b/src/silx/gui/plot/matplotlib/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/setup.py b/src/silx/gui/plot/setup.py deleted file mode 100644 index e0b2c91..0000000 --- a/src/silx/gui/plot/setup.py +++ /dev/null @@ -1,54 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-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. -# -# ###########################################################################*/ -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "29/06/2017" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('plot', parent_package, top_path) - config.add_subpackage('_utils') - config.add_subpackage('utils') - config.add_subpackage('matplotlib') - config.add_subpackage('stats') - config.add_subpackage('backends') - config.add_subpackage('backends.glutils') - config.add_subpackage('items') - config.add_subpackage('test') - config.add_subpackage('tools') - config.add_subpackage('tools.profile') - config.add_subpackage('tools.test') - config.add_subpackage('actions') - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/gui/plot/stats/__init__.py b/src/silx/gui/plot/stats/__init__.py index 04a5327..dfaa865 100644 --- a/src/silx/gui/plot/stats/__init__.py +++ b/src/silx/gui/plot/stats/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/stats/stats.py b/src/silx/gui/plot/stats/stats.py index a81f7bb..d266d5c 100644 --- a/src/silx/gui/plot/stats/stats.py +++ b/src/silx/gui/plot/stats/stats.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -871,7 +870,7 @@ class StatCOM(StatBase): values = numpy.ma.array(context.values, mask=context.mask, dtype=numpy.float64) sum_ = numpy.sum(values) - if sum_ == 0.: + if sum_ == 0. or numpy.ma.is_masked(sum_): return (numpy.nan,) * len(context.axes) if context.isStructuredData(): diff --git a/src/silx/gui/plot/stats/statshandler.py b/src/silx/gui/plot/stats/statshandler.py index 17578d8..1531ba2 100644 --- a/src/silx/gui/plot/stats/statshandler.py +++ b/src/silx/gui/plot/stats/statshandler.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2019 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -32,6 +31,9 @@ __date__ = "05/06/2018" import logging +import numbers + +import numpy from silx.gui import qt from silx.gui.plot import stats as statsmdl @@ -72,11 +74,14 @@ class StatFormatter(object): self.tabWidgetItemClass = qItemClass def format(self, val): - if self.formatter is None or val is None: - return str(val) - else: + if val is None or numpy.ma.is_masked(val): + return "--" + + if self.formatter is not None and isinstance(val, numbers.Number): return self.formatter.format(val) + return str(val) + class StatsHandler(object): """ diff --git a/src/silx/gui/plot/test/__init__.py b/src/silx/gui/plot/test/__init__.py index 3ad225d..78821ec 100644 --- a/src/silx/gui/plot/test/__init__.py +++ b/src/silx/gui/plot/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testAlphaSlider.py b/src/silx/gui/plot/test/testAlphaSlider.py index ca57bf5..8641da7 100644 --- a/src/silx/gui/plot/test/testAlphaSlider.py +++ b/src/silx/gui/plot/test/testAlphaSlider.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testColorBar.py b/src/silx/gui/plot/test/testColorBar.py index 3dc8ff1..199726b 100644 --- a/src/silx/gui/plot/test/testColorBar.py +++ b/src/silx/gui/plot/test/testColorBar.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testCompareImages.py b/src/silx/gui/plot/test/testCompareImages.py index cf54b99..9b5065d 100644 --- a/src/silx/gui/plot/test/testCompareImages.py +++ b/src/silx/gui/plot/test/testCompareImages.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testComplexImageView.py b/src/silx/gui/plot/test/testComplexImageView.py index 46025b9..c26df25 100644 --- a/src/silx/gui/plot/test/testComplexImageView.py +++ b/src/silx/gui/plot/test/testComplexImageView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testCurvesROIWidget.py b/src/silx/gui/plot/test/testCurvesROIWidget.py index d7dfafd..32ac057 100644 --- a/src/silx/gui/plot/test/testCurvesROIWidget.py +++ b/src/silx/gui/plot/test/testCurvesROIWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -348,6 +347,8 @@ class TestRoiWidgetSignals(TestCaseQt): """Test Signals emitted by the RoiWidgetSignals""" def setUp(self): + super().setUp() + self.plot = Plot1D() x = range(20) y = range(20) @@ -368,8 +369,15 @@ class TestRoiWidgetSignals(TestCaseQt): self.qWaitForWindowExposed(self.curves_roi_widget) def tearDown(self): - self.plot = None - self.curves_roi_widget = None + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.close() + del self.plot + + self.curves_roi_widget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.curves_roi_widget.close() + del self.curves_roi_widget + + super().tearDown() def testSigROISignalAddRmRois(self): """Test SigROISignal when adding and removing ROIS""" diff --git a/src/silx/gui/plot/test/testImageStack.py b/src/silx/gui/plot/test/testImageStack.py index 5c44691..702f0fe 100644 --- a/src/silx/gui/plot/test/testImageStack.py +++ b/src/silx/gui/plot/test/testImageStack.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testImageView.py b/src/silx/gui/plot/test/testImageView.py index 7c1355f..9fb6a5d 100644 --- a/src/silx/gui/plot/test/testImageView.py +++ b/src/silx/gui/plot/test/testImageView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testInteraction.py b/src/silx/gui/plot/test/testInteraction.py index d136b21..459b132 100644 --- a/src/silx/gui/plot/test/testInteraction.py +++ b/src/silx/gui/plot/test/testInteraction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testItem.py b/src/silx/gui/plot/test/testItem.py index 0b15dc3..7b4f636 100644 --- a/src/silx/gui/plot/test/testItem.py +++ b/src/silx/gui/plot/test/testItem.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testLegendSelector.py b/src/silx/gui/plot/test/testLegendSelector.py index c40875d..3a596ac 100644 --- a/src/silx/gui/plot/test/testLegendSelector.py +++ b/src/silx/gui/plot/test/testLegendSelector.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testLimitConstraints.py b/src/silx/gui/plot/test/testLimitConstraints.py index 0bd8e50..04a53e1 100644 --- a/src/silx/gui/plot/test/testLimitConstraints.py +++ b/src/silx/gui/plot/test/testLimitConstraints.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testMaskToolsWidget.py b/src/silx/gui/plot/test/testMaskToolsWidget.py index 522ca51..5f36ec2 100644 --- a/src/silx/gui/plot/test/testMaskToolsWidget.py +++ b/src/silx/gui/plot/test/testMaskToolsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testPixelIntensityHistoAction.py b/src/silx/gui/plot/test/testPixelIntensityHistoAction.py index 14a467d..43d7588 100644 --- a/src/silx/gui/plot/test/testPixelIntensityHistoAction.py +++ b/src/silx/gui/plot/test/testPixelIntensityHistoAction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testPlotActions.py b/src/silx/gui/plot/test/testPlotActions.py index f38e05b..4006ab9 100644 --- a/src/silx/gui/plot/test/testPlotActions.py +++ b/src/silx/gui/plot/test/testPlotActions.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testPlotInteraction.py b/src/silx/gui/plot/test/testPlotInteraction.py index fba364e..17aad97 100644 --- a/src/silx/gui/plot/test/testPlotInteraction.py +++ b/src/silx/gui/plot/test/testPlotInteraction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016=2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testPlotWidget.py b/src/silx/gui/plot/test/testPlotWidget.py index f6e108d..19a34a9 100755 --- a/src/silx/gui/plot/test/testPlotWidget.py +++ b/src/silx/gui/plot/test/testPlotWidget.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -1651,6 +1650,26 @@ class TestPlotCurveLog(PlotWidgetTestCase, ParametricTestCase): self.qapp.processEvents() + if xError is None: + dataMin, dataMax = numpy.min(self.xData), numpy.max(self.xData) + else: + xMinusError = self.xData - numpy.atleast_2d(xError)[0] + dataMin = numpy.min(xMinusError[xMinusError > 0]) + xPlusError = self.xData + numpy.atleast_2d(xError)[-1] + dataMax = numpy.max(xPlusError[xPlusError > 0]) + plotMin, plotMax = self.plot.getXAxis().getLimits() + assert numpy.allclose((dataMin, dataMax), (plotMin, plotMax)) + + if yError is None: + dataMin, dataMax = numpy.min(self.yData), numpy.max(self.yData) + else: + yMinusError = self.yData - numpy.atleast_2d(yError)[0] + dataMin = numpy.min(yMinusError[yMinusError > 0]) + yPlusError = self.yData + numpy.atleast_2d(yError)[-1] + dataMax = numpy.max(yPlusError[yPlusError > 0]) + plotMin, plotMax = self.plot.getYAxis().getLimits() + assert numpy.allclose((dataMin, dataMax), (plotMin, plotMax)) + self.plot.clear() self.plot.resetZoom() self.qapp.processEvents() diff --git a/src/silx/gui/plot/test/testPlotWidgetNoBackend.py b/src/silx/gui/plot/test/testPlotWidgetNoBackend.py index 4914929..787d5a8 100644 --- a/src/silx/gui/plot/test/testPlotWidgetNoBackend.py +++ b/src/silx/gui/plot/test/testPlotWidgetNoBackend.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testPlotWindow.py b/src/silx/gui/plot/test/testPlotWindow.py index 9e1497f..8e3f1df 100644 --- a/src/silx/gui/plot/test/testPlotWindow.py +++ b/src/silx/gui/plot/test/testPlotWindow.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testRoiStatsWidget.py b/src/silx/gui/plot/test/testRoiStatsWidget.py index eb29267..2c1c6b3 100644 --- a/src/silx/gui/plot/test/testRoiStatsWidget.py +++ b/src/silx/gui/plot/test/testRoiStatsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testSaveAction.py b/src/silx/gui/plot/test/testSaveAction.py index 9280fb6..d5a06c6 100644 --- a/src/silx/gui/plot/test/testSaveAction.py +++ b/src/silx/gui/plot/test/testSaveAction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testScatterMaskToolsWidget.py b/src/silx/gui/plot/test/testScatterMaskToolsWidget.py index 447ee58..68375b0 100644 --- a/src/silx/gui/plot/test/testScatterMaskToolsWidget.py +++ b/src/silx/gui/plot/test/testScatterMaskToolsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testScatterView.py b/src/silx/gui/plot/test/testScatterView.py index d11d4d8..692612d 100644 --- a/src/silx/gui/plot/test/testScatterView.py +++ b/src/silx/gui/plot/test/testScatterView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testStackView.py b/src/silx/gui/plot/test/testStackView.py index 0d18113..aba8678 100644 --- a/src/silx/gui/plot/test/testStackView.py +++ b/src/silx/gui/plot/test/testStackView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testStats.py b/src/silx/gui/plot/test/testStats.py index 0a792a4..c5d5181 100644 --- a/src/silx/gui/plot/test/testStats.py +++ b/src/silx/gui/plot/test/testStats.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/testUtilsAxis.py b/src/silx/gui/plot/test/testUtilsAxis.py index dd4a689..879ec73 100644 --- a/src/silx/gui/plot/test/testUtilsAxis.py +++ b/src/silx/gui/plot/test/testUtilsAxis.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/test/utils.py b/src/silx/gui/plot/test/utils.py index 64fca56..faa40bb 100644 --- a/src/silx/gui/plot/test/utils.py +++ b/src/silx/gui/plot/test/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/CurveLegendsWidget.py b/src/silx/gui/plot/tools/CurveLegendsWidget.py index 4a517dd..c9b0101 100644 --- a/src/silx/gui/plot/tools/CurveLegendsWidget.py +++ b/src/silx/gui/plot/tools/CurveLegendsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a widget to display :class:`PlotWidget` curve legends. """ -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "20/07/2018" diff --git a/src/silx/gui/plot/tools/LimitsToolBar.py b/src/silx/gui/plot/tools/LimitsToolBar.py index fc192a6..d7f4bf5 100644 --- a/src/silx/gui/plot/tools/LimitsToolBar.py +++ b/src/silx/gui/plot/tools/LimitsToolBar.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -25,9 +24,6 @@ """A toolbar to display and edit limits of a PlotWidget """ - -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" __date__ = "16/10/2017" diff --git a/src/silx/gui/plot/tools/PositionInfo.py b/src/silx/gui/plot/tools/PositionInfo.py index 8b95fbc..cb16b80 100644 --- a/src/silx/gui/plot/tools/PositionInfo.py +++ b/src/silx/gui/plot/tools/PositionInfo.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -27,8 +26,6 @@ It can be configured to provide more information. """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" __date__ = "16/10/2017" @@ -239,7 +236,7 @@ class PositionInfo(qt.QWidget): ratio = qt.QGuiApplication.primaryScreen().devicePixelRatio() # Baseline squared distance threshold - distInPixels = (self.SNAP_THRESHOLD_DIST * ratio)**2 + sqDistInPixels = (self.SNAP_THRESHOLD_DIST * ratio)**2 for item in selectedItems: if (snappingMode & self.SNAPPING_SYMBOLS_ONLY and ( @@ -263,33 +260,36 @@ class PositionInfo(qt.QWidget): break else: # Curve, Scatter - xArray = item.getXData(copy=False) - yArray = item.getYData(copy=False) - closestIndex = numpy.argmin( - pow(xArray - x, 2) + pow(yArray - y, 2)) - - xClosest = xArray[closestIndex] - yClosest = yArray[closestIndex] + result = item.pick(xPixel, yPixel) + if result is None: + continue + indices = result.getIndices(copy=False) + if indices is None: + continue if isinstance(item, items.YAxisMixIn): axis = item.getYAxis() else: axis = 'left' - closestInPixels = plot.dataToPixel( - xClosest, yClosest, axis=axis) - if closestInPixels is not None: - curveDistInPixels = ( - (closestInPixels[0] - xPixel)**2 + - (closestInPixels[1] - yPixel)**2) - - if curveDistInPixels <= distInPixels: - # Update label style sheet - styleSheet = "color: rgb(0, 0, 0);" + xArray = item.getXData(copy=False)[indices] + yArray = item.getYData(copy=False)[indices] + pixelPositions = plot.dataToPixel(xArray, yArray, axis=axis) + if pixelPositions is None: + continue + sqDistances = (pixelPositions[0] - xPixel)**2 + (pixelPositions[1] - yPixel)**2 + if not numpy.any(numpy.isfinite(sqDistances)): + continue + closestIndex = numpy.nanargmin(sqDistances) + closestSqDistInPixels = sqDistances[closestIndex] + + if closestSqDistInPixels <= sqDistInPixels: + # Update label style sheet + styleSheet = "color: rgb(0, 0, 0);" - # if close enough, snap to data point coord - xData, yData = xClosest, yClosest - distInPixels = curveDistInPixels + # if close enough, snap to data point coord + xData, yData = xArray[closestIndex], yArray[closestIndex] + sqDistInPixels = closestSqDistInPixels for label, name, func in self._fields: label.setStyleSheet(styleSheet) diff --git a/src/silx/gui/plot/tools/RadarView.py b/src/silx/gui/plot/tools/RadarView.py index 7076835..886f37e 100644 --- a/src/silx/gui/plot/tools/RadarView.py +++ b/src/silx/gui/plot/tools/RadarView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/__init__.py b/src/silx/gui/plot/tools/__init__.py index 09f468c..5b6b74c 100644 --- a/src/silx/gui/plot/tools/__init__.py +++ b/src/silx/gui/plot/tools/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/profile/ScatterProfileToolBar.py b/src/silx/gui/plot/tools/profile/ScatterProfileToolBar.py index 44187ef..09f90b7 100644 --- a/src/silx/gui/plot/tools/profile/ScatterProfileToolBar.py +++ b/src/silx/gui/plot/tools/profile/ScatterProfileToolBar.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/profile/__init__.py b/src/silx/gui/plot/tools/profile/__init__.py index d91191e..a72b5d2 100644 --- a/src/silx/gui/plot/tools/profile/__init__.py +++ b/src/silx/gui/plot/tools/profile/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/profile/core.py b/src/silx/gui/plot/tools/profile/core.py index 200f5cf..5d4a674 100644 --- a/src/silx/gui/plot/tools/profile/core.py +++ b/src/silx/gui/plot/tools/profile/core.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/profile/editors.py b/src/silx/gui/plot/tools/profile/editors.py index 80e0452..1d6f198 100644 --- a/src/silx/gui/plot/tools/profile/editors.py +++ b/src/silx/gui/plot/tools/profile/editors.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility @@ -252,7 +251,10 @@ class ProfileRoiEditorAction(qt.QWidgetAction): return layout = widget.layout() if previousEditor is not None: - previousEditor.sigDataCommited.disconnect(self._editorDataCommited) + try: + previousEditor.sigDataCommited.disconnect(self._editorDataCommited) + except (RuntimeError, TypeError): + pass layout.removeWidget(previousEditor) previousEditor.deleteLater() if editor is not None: diff --git a/src/silx/gui/plot/tools/profile/manager.py b/src/silx/gui/plot/tools/profile/manager.py index 4a22bc0..58c1c86 100644 --- a/src/silx/gui/plot/tools/profile/manager.py +++ b/src/silx/gui/plot/tools/profile/manager.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -180,6 +179,8 @@ class ProfileWindow(qt.QMainWindow): plot.setDataMargins(yMinMargin=0.1, yMaxMargin=0.1) plot.setGraphYLabel('Profile') plot.setGraphXLabel('') + positionInfo = plot.getPositionInfoWidget() + positionInfo.setSnappingMode(positionInfo.SNAPPING_CURVE) return plot def createPlot2D(self, parent, backend): diff --git a/src/silx/gui/plot/tools/profile/rois.py b/src/silx/gui/plot/tools/profile/rois.py index 9eef622..042aff1 100644 --- a/src/silx/gui/plot/tools/profile/rois.py +++ b/src/silx/gui/plot/tools/profile/rois.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -945,13 +944,19 @@ class _DefaultScatterProfileSliceRoiMixIn(core.ProfileRoiMixIn): if major_axis: # slice in the middle of the scatter - start = max_grid_second // 2 * max_grid_first - vslice = axis[start:start + max_grid_second] + actual_size_grid_second = len(axis) // max_grid_first + start = actual_size_grid_second // 2 * max_grid_first + vslice = axis[start:start + max_grid_first] + if len(vslice) == 0: + return None index = argnearest(vslice, position) slicing = slice(index, None, max_grid_first) else: # slice in the middle of the scatter - vslice = axis[max_grid_second // 2::max_grid_second] + actual_size_grid_second = len(axis) // max_grid_first + vslice = axis[actual_size_grid_second // 2::max_grid_second] + if len(vslice) == 0: + return None index = argnearest(vslice, position) start = index * max_grid_second slicing = slice(start, start + max_grid_second) @@ -1086,11 +1091,14 @@ class _DefaultImageStackProfileRoiMixIn(_DefaultImageProfileRoiMixIn): coords, profile, profileName, xLabel = createProfile2(currentData) + profileManager = self.getProfileManager() + plot = profileManager.getPlotWidget() + data = core.ImageProfileData( coords=coords, profile=profile, - title=profileName, - xLabel=xLabel, + title=_relabelAxes(plot, profileName), + xLabel=_relabelAxes(plot, xLabel), yLabel="Profile", colormap=colormap, ) diff --git a/src/silx/gui/plot/tools/profile/toolbar.py b/src/silx/gui/plot/tools/profile/toolbar.py index 4a9a195..12a734a 100644 --- a/src/silx/gui/plot/tools/profile/toolbar.py +++ b/src/silx/gui/plot/tools/profile/toolbar.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/roi.py b/src/silx/gui/plot/tools/roi.py index e4be6a7..1da692c 100644 --- a/src/silx/gui/plot/tools/roi.py +++ b/src/silx/gui/plot/tools/roi.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2018-2021 European Synchrotron Radiation Facility +# Copyright (c) 2018-2022 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 @@ -381,6 +380,7 @@ class RegionOfInterestManager(qt.QObject): roi_items.VerticalLineROI, roi_items.ArcROI, roi_items.HorizontalRangeROI, + roi_items.BandROI, ) def __init__(self, parent): diff --git a/src/silx/gui/plot/tools/test/__init__.py b/src/silx/gui/plot/tools/test/__init__.py index aa4a601..2e682d7 100644 --- a/src/silx/gui/plot/tools/test/__init__.py +++ b/src/silx/gui/plot/tools/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/test/testCurveLegendsWidget.py b/src/silx/gui/plot/tools/test/testCurveLegendsWidget.py index 37af10e..657d328 100644 --- a/src/silx/gui/plot/tools/test/testCurveLegendsWidget.py +++ b/src/silx/gui/plot/tools/test/testCurveLegendsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/test/testProfile.py b/src/silx/gui/plot/tools/test/testProfile.py index 829f49e..ad40e67 100644 --- a/src/silx/gui/plot/tools/test/testProfile.py +++ b/src/silx/gui/plot/tools/test/testProfile.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/test/testROI.py b/src/silx/gui/plot/tools/test/testROI.py index 21697d1..6ce1553 100644 --- a/src/silx/gui/plot/tools/test/testROI.py +++ b/src/silx/gui/plot/tools/test/testROI.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility @@ -268,6 +267,12 @@ class TestRoiItems(TestCaseQt): self.assertAlmostEqual(item.getMax(), vmax) self.assertAlmostEqual(item.getCenter(), 2) + def testBand_getToSetGeometry(self): + """Test that we can use getGeometry as input to setGeometry""" + item = roi_items.BandROI() + item.setFirstShapePoints(numpy.array([[5, 10], [50, 100]])) + item.setGeometry(*item.getGeometry()) + class TestRegionOfInterestManager(TestCaseQt, ParametricTestCase): """Tests for RegionOfInterestManager class""" @@ -577,7 +582,7 @@ class TestRegionOfInterestManager(TestCaseQt, ParametricTestCase): manager.addRoi(item) self.qapp.processEvents() - # Drag the center + # Drag the center widget = self.plot.getWidgetHandle() mx, my = self.plot.dataToPixel(*center) self.mouseMove(widget, pos=(mx, my)) @@ -680,3 +685,56 @@ class TestRegionOfInterestManager(TestCaseQt, ParametricTestCase): manager.clear() self.qapp.processEvents() + + def testBandRoiSwitchMode(self): + """Make sure we can switch mode by clicking on the ROI""" + xlimit = self.plot.getXAxis().getLimits() + ylimit = self.plot.getYAxis().getLimits() + xcenter = 0.5 * (xlimit[0] + xlimit[1]) + ycenter = 0.5 * (ylimit[0] + ylimit[1]) + + # Create the line + manager = roi.RegionOfInterestManager(self.plot) + item = roi_items.BandROI() + item.setGeometry( + (xlimit[0], ycenter), + (xlimit[1], ycenter), + 20, + ) + item.setEditable(True) + item.setSelectable(True) + manager.addRoi(item) + self.qapp.processEvents() + + # Initial state + assert item.getInteractionMode() is roi_items.BandROI.BoundedMode + self.qWait(500) + + # Click on the center + widget = self.plot.getWidgetHandle() + mx, my = self.plot.dataToPixel(xcenter, ycenter) + + # Select the ROI + self.mouseMove(widget, pos=(mx, my)) + self.mouseClick(widget, qt.Qt.LeftButton, pos=(mx, my)) + self.qWait(500) + assert item.getInteractionMode() is roi_items.BandROI.BoundedMode + + # Change the mode + self.mouseMove(widget, pos=(mx, my)) + self.mouseClick(widget, qt.Qt.LeftButton, pos=(mx, my)) + self.qWait(500) + assert item.getInteractionMode() is roi_items.BandROI.UnboundedMode + + # Set available modes that exclude the current one + item.setAvailableInteractionModes([roi_items.BandROI.BoundedMode]) + assert item.getInteractionMode() is roi_items.BandROI.BoundedMode + + # Clicking does not change the mode since there is only one + self.mouseMove(widget, pos=(mx, my)) + self.mouseClick(widget, qt.Qt.LeftButton, pos=(mx, my)) + self.qWait(500) + assert item.getInteractionMode() is roi_items.BandROI.BoundedMode + + manager.clear() + self.qapp.processEvents() diff --git a/src/silx/gui/plot/tools/test/testScatterProfileToolBar.py b/src/silx/gui/plot/tools/test/testScatterProfileToolBar.py index 582a276..9b9caa1 100644 --- a/src/silx/gui/plot/tools/test/testScatterProfileToolBar.py +++ b/src/silx/gui/plot/tools/test/testScatterProfileToolBar.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/test/testTools.py b/src/silx/gui/plot/tools/test/testTools.py index 846f641..507b922 100644 --- a/src/silx/gui/plot/tools/test/testTools.py +++ b/src/silx/gui/plot/tools/test/testTools.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/tools/toolbars.py b/src/silx/gui/plot/tools/toolbars.py index 3df7d06..bb89942 100644 --- a/src/silx/gui/plot/tools/toolbars.py +++ b/src/silx/gui/plot/tools/toolbars.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/utils/__init__.py b/src/silx/gui/plot/utils/__init__.py index 3187f6b..61e45b4 100644 --- a/src/silx/gui/plot/utils/__init__.py +++ b/src/silx/gui/plot/utils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/utils/axis.py b/src/silx/gui/plot/utils/axis.py index 5cf8ad9..419a71c 100644 --- a/src/silx/gui/plot/utils/axis.py +++ b/src/silx/gui/plot/utils/axis.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot/utils/intersections.py b/src/silx/gui/plot/utils/intersections.py index 53f2546..4f6ed23 100644 --- a/src/silx/gui/plot/utils/intersections.py +++ b/src/silx/gui/plot/utils/intersections.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/ParamTreeView.py b/src/silx/gui/plot3d/ParamTreeView.py index 2593860..b648251 100644 --- a/src/silx/gui/plot3d/ParamTreeView.py +++ b/src/silx/gui/plot3d/ParamTreeView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -33,8 +32,6 @@ This module contains: :class:`Vector4DEditor`, :class:`IntSliderEditor`, :class:`BooleanEditor` """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "05/12/2017" diff --git a/src/silx/gui/plot3d/Plot3DWidget.py b/src/silx/gui/plot3d/Plot3DWidget.py index a90d34c..09e06a2 100644 --- a/src/silx/gui/plot3d/Plot3DWidget.py +++ b/src/silx/gui/plot3d/Plot3DWidget.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2015-2021 European Synchrotron Radiation Facility +# Copyright (c) 2015-2022 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 @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides a Qt widget embedding an OpenGL scene.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" @@ -125,7 +122,7 @@ class Plot3DWidget(glu.OpenGLWidget): LINEAR = 'linear' """Linear fog through the whole scene""" - def __init__(self, parent=None, f=qt.Qt.WindowFlags()): + def __init__(self, parent=None, f=qt.Qt.Widget): self._firstRender = True super(Plot3DWidget, self).__init__( @@ -380,10 +377,7 @@ class Plot3DWidget(glu.OpenGLWidget): return convertArrayToQImage(image) def wheelEvent(self, event): - if qt.BINDING == "PySide6": - x, y = event.position().x(), event.position().y() - else: - x, y = event.x(), event.y() + x, y = qt.getMouseEventPosition(event) xpixel = x * self.getDevicePixelRatio() ypixel = y * self.getDevicePixelRatio() angle = event.angleDelta().y() / 8. @@ -431,11 +425,16 @@ class Plot3DWidget(glu.OpenGLWidget): super(Plot3DWidget, self).keyReleaseEvent(event) # Mouse events # - _MOUSE_BTNS = {1: 'left', 2: 'right', 4: 'middle'} + _MOUSE_BTNS = { + qt.Qt.LeftButton: 'left', + qt.Qt.RightButton: 'right', + qt.Qt.MiddleButton: 'middle', + } def mousePressEvent(self, event): - xpixel = event.x() * self.getDevicePixelRatio() - ypixel = event.y() * self.getDevicePixelRatio() + x, y = qt.getMouseEventPosition(event) + xpixel = x * self.getDevicePixelRatio() + ypixel = y * self.getDevicePixelRatio() btn = self._MOUSE_BTNS[event.button()] event.accept() @@ -444,8 +443,9 @@ class Plot3DWidget(glu.OpenGLWidget): self.eventHandler.handleEvent('press', xpixel, ypixel, btn) def mouseMoveEvent(self, event): - xpixel = event.x() * self.getDevicePixelRatio() - ypixel = event.y() * self.getDevicePixelRatio() + x, y = qt.getMouseEventPosition(event) + xpixel = x * self.getDevicePixelRatio() + ypixel = y * self.getDevicePixelRatio() event.accept() if self.eventHandler is not None and self.isValid(): @@ -453,8 +453,9 @@ class Plot3DWidget(glu.OpenGLWidget): self.eventHandler.handleEvent('move', xpixel, ypixel) def mouseReleaseEvent(self, event): - xpixel = event.x() * self.getDevicePixelRatio() - ypixel = event.y() * self.getDevicePixelRatio() + x, y = qt.getMouseEventPosition(event) + xpixel = x * self.getDevicePixelRatio() + ypixel = y * self.getDevicePixelRatio() btn = self._MOUSE_BTNS[event.button()] event.accept() diff --git a/src/silx/gui/plot3d/Plot3DWindow.py b/src/silx/gui/plot3d/Plot3DWindow.py index 470b966..882f4cd 100644 --- a/src/silx/gui/plot3d/Plot3DWindow.py +++ b/src/silx/gui/plot3d/Plot3DWindow.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2019 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a QMainWindow with a 3D scene and associated toolbar. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "26/01/2017" diff --git a/src/silx/gui/plot3d/SFViewParamTree.py b/src/silx/gui/plot3d/SFViewParamTree.py index b269a6a..cc78cec 100644 --- a/src/silx/gui/plot3d/SFViewParamTree.py +++ b/src/silx/gui/plot3d/SFViewParamTree.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2021 European Synchrotron Radiation Facility @@ -26,8 +25,6 @@ This module provides a tree widget to set/view parameters of a ScalarFieldView. """ -from __future__ import absolute_import - __authors__ = ["D. N."] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/ScalarFieldView.py b/src/silx/gui/plot3d/ScalarFieldView.py index b2bb254..0633221 100644 --- a/src/silx/gui/plot3d/ScalarFieldView.py +++ b/src/silx/gui/plot3d/ScalarFieldView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2020 European Synchrotron Radiation Facility @@ -28,8 +27,6 @@ It supports iso-surfaces, a cutting plane and the definition of a region of interest. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "14/06/2018" diff --git a/src/silx/gui/plot3d/SceneWidget.py b/src/silx/gui/plot3d/SceneWidget.py index 883f5e7..910820c 100644 --- a/src/silx/gui/plot3d/SceneWidget.py +++ b/src/silx/gui/plot3d/SceneWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2019 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides a widget to view data sets in 3D.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/SceneWindow.py b/src/silx/gui/plot3d/SceneWindow.py index 052a4dc..d88cfa9 100644 --- a/src/silx/gui/plot3d/SceneWindow.py +++ b/src/silx/gui/plot3d/SceneWindow.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2019 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a QMainWindow with a 3D SceneWidget and toolbars. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "29/11/2017" diff --git a/src/silx/gui/plot3d/__init__.py b/src/silx/gui/plot3d/__init__.py index af74613..e0cb688 100644 --- a/src/silx/gui/plot3d/__init__.py +++ b/src/silx/gui/plot3d/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility @@ -27,7 +26,6 @@ This package provides widgets displaying 3D content based on OpenGL. It depends on PyOpenGL and PyQtx.QtOpenGL or PyQt>=5.4. """ -from __future__ import absolute_import __authors__ = ["T. Vincent"] __license__ = "MIT" diff --git a/src/silx/gui/plot3d/_model/__init__.py b/src/silx/gui/plot3d/_model/__init__.py index 4b16e32..fd8eafb 100644 --- a/src/silx/gui/plot3d/_model/__init__.py +++ b/src/silx/gui/plot3d/_model/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility @@ -26,8 +25,6 @@ This package provides :class:`SceneWidget` content and parameters model. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "11/01/2018" diff --git a/src/silx/gui/plot3d/_model/core.py b/src/silx/gui/plot3d/_model/core.py index e8e0820..30d45ec 100644 --- a/src/silx/gui/plot3d/_model/core.py +++ b/src/silx/gui/plot3d/_model/core.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility @@ -26,8 +25,6 @@ This module provides base classes to implement models for 3D scene content. """ -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "11/01/2018" diff --git a/src/silx/gui/plot3d/_model/items.py b/src/silx/gui/plot3d/_model/items.py index 492f44b..c6bf69a 100644 --- a/src/silx/gui/plot3d/_model/items.py +++ b/src/silx/gui/plot3d/_model/items.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -26,8 +25,6 @@ This module provides base classes to implement models for 3D scene content """ -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/_model/model.py b/src/silx/gui/plot3d/_model/model.py index 186838f..5276878 100644 --- a/src/silx/gui/plot3d/_model/model.py +++ b/src/silx/gui/plot3d/_model/model.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility @@ -26,8 +25,6 @@ This module provides the :class:`SceneWidget` content and parameters model. """ -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "11/01/2018" diff --git a/src/silx/gui/plot3d/actions/Plot3DAction.py b/src/silx/gui/plot3d/actions/Plot3DAction.py index 94b9572..a2ee93c 100644 --- a/src/silx/gui/plot3d/actions/Plot3DAction.py +++ b/src/silx/gui/plot3d/actions/Plot3DAction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Base class for QAction attached to a Plot3DWidget.""" -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "06/09/2017" diff --git a/src/silx/gui/plot3d/actions/__init__.py b/src/silx/gui/plot3d/actions/__init__.py index 26243cf..e6c7312 100644 --- a/src/silx/gui/plot3d/actions/__init__.py +++ b/src/silx/gui/plot3d/actions/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/actions/io.py b/src/silx/gui/plot3d/actions/io.py index 25f4ade..f8a1d86 100644 --- a/src/silx/gui/plot3d/actions/io.py +++ b/src/silx/gui/plot3d/actions/io.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -27,8 +26,6 @@ It provides QAction to copy, save (snapshot and video), print a Plot3DWidget. """ -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "06/09/2017" diff --git a/src/silx/gui/plot3d/actions/mode.py b/src/silx/gui/plot3d/actions/mode.py index b9cd7c8..179fe05 100644 --- a/src/silx/gui/plot3d/actions/mode.py +++ b/src/silx/gui/plot3d/actions/mode.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -28,8 +27,6 @@ It provides QAction to rotate or pan a Plot3DWidget as well as toggle a picking mode. """ -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "06/09/2017" diff --git a/src/silx/gui/plot3d/actions/viewpoint.py b/src/silx/gui/plot3d/actions/viewpoint.py index d764c40..c3d640e 100644 --- a/src/silx/gui/plot3d/actions/viewpoint.py +++ b/src/silx/gui/plot3d/actions/viewpoint.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility @@ -27,8 +26,6 @@ It provides QAction to rotate or pan a Plot3DWidget. """ -from __future__ import absolute_import, division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "03/10/2017" diff --git a/src/silx/gui/plot3d/items/__init__.py b/src/silx/gui/plot3d/items/__init__.py index e7c4af1..3d22103 100644 --- a/src/silx/gui/plot3d/items/__init__.py +++ b/src/silx/gui/plot3d/items/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This package provides classes that describes :class:`.SceneWidget` content. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "15/11/2017" diff --git a/src/silx/gui/plot3d/items/_pick.py b/src/silx/gui/plot3d/items/_pick.py index 0d6a495..49e1a5b 100644 --- a/src/silx/gui/plot3d/items/_pick.py +++ b/src/silx/gui/plot3d/items/_pick.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides classes supporting item picking. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/09/2018" diff --git a/src/silx/gui/plot3d/items/clipplane.py b/src/silx/gui/plot3d/items/clipplane.py index 3e819d0..83a3c0e 100644 --- a/src/silx/gui/plot3d/items/clipplane.py +++ b/src/silx/gui/plot3d/items/clipplane.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a scene clip plane class. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "15/11/2017" diff --git a/src/silx/gui/plot3d/items/core.py b/src/silx/gui/plot3d/items/core.py index 0388ce7..5fbe62c 100644 --- a/src/silx/gui/plot3d/items/core.py +++ b/src/silx/gui/plot3d/items/core.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides the base class for items of the :class:`.SceneWidget`. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "15/11/2017" diff --git a/src/silx/gui/plot3d/items/image.py b/src/silx/gui/plot3d/items/image.py index 5a50459..669e97d 100644 --- a/src/silx/gui/plot3d/items/image.py +++ b/src/silx/gui/plot3d/items/image.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides 2D data and RGB(A) image item class. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "15/11/2017" diff --git a/src/silx/gui/plot3d/items/mesh.py b/src/silx/gui/plot3d/items/mesh.py index 4e19939..dc1df3e 100644 --- a/src/silx/gui/plot3d/items/mesh.py +++ b/src/silx/gui/plot3d/items/mesh.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides regular mesh item class. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/07/2018" diff --git a/src/silx/gui/plot3d/items/mixins.py b/src/silx/gui/plot3d/items/mixins.py index f512365..45b569d 100644 --- a/src/silx/gui/plot3d/items/mixins.py +++ b/src/silx/gui/plot3d/items/mixins.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/items/scatter.py b/src/silx/gui/plot3d/items/scatter.py index 24abaa5..c93db88 100644 --- a/src/silx/gui/plot3d/items/scatter.py +++ b/src/silx/gui/plot3d/items/scatter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides 2D and 3D scatter data item class. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "15/11/2017" diff --git a/src/silx/gui/plot3d/items/volume.py b/src/silx/gui/plot3d/items/volume.py index f80fea2..b3007fa 100644 --- a/src/silx/gui/plot3d/items/volume.py +++ b/src/silx/gui/plot3d/items/volume.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides 3D array item class and its sub-items. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/scene/__init__.py b/src/silx/gui/plot3d/scene/__init__.py index 9671725..9f7c470 100644 --- a/src/silx/gui/plot3d/scene/__init__.py +++ b/src/silx/gui/plot3d/scene/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/scene/axes.py b/src/silx/gui/plot3d/scene/axes.py index e35e5e1..9f6ac6c 100644 --- a/src/silx/gui/plot3d/scene/axes.py +++ b/src/silx/gui/plot3d/scene/axes.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Primitive displaying a text field in the scene.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/10/2016" diff --git a/src/silx/gui/plot3d/scene/camera.py b/src/silx/gui/plot3d/scene/camera.py index 90de7ed..a6bc642 100644 --- a/src/silx/gui/plot3d/scene/camera.py +++ b/src/silx/gui/plot3d/scene/camera.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides classes to handle a perspective projection in 3D.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "25/07/2016" diff --git a/src/silx/gui/plot3d/scene/core.py b/src/silx/gui/plot3d/scene/core.py index 43838fe..c32a2c1 100644 --- a/src/silx/gui/plot3d/scene/core.py +++ b/src/silx/gui/plot3d/scene/core.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2019 European Synchrotron Radiation Facility @@ -32,8 +31,6 @@ Nodes with children are provided with :class:`PrivateGroup` and Leaf rendering nodes should inherit from :class:`Elem`. """ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "25/07/2016" diff --git a/src/silx/gui/plot3d/scene/cutplane.py b/src/silx/gui/plot3d/scene/cutplane.py index 88147df..bfd578f 100644 --- a/src/silx/gui/plot3d/scene/cutplane.py +++ b/src/silx/gui/plot3d/scene/cutplane.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """A cut plane in a 3D texture: hackish implementation... """ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "11/01/2018" diff --git a/src/silx/gui/plot3d/scene/event.py b/src/silx/gui/plot3d/scene/event.py index 98f8f8b..637eddf 100644 --- a/src/silx/gui/plot3d/scene/event.py +++ b/src/silx/gui/plot3d/scene/event.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides a simple generic notification system.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/07/2018" diff --git a/src/silx/gui/plot3d/scene/function.py b/src/silx/gui/plot3d/scene/function.py index 2deb785..3d0a62f 100644 --- a/src/silx/gui/plot3d/scene/function.py +++ b/src/silx/gui/plot3d/scene/function.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2020 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides functions to add to shaders.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/07/2018" diff --git a/src/silx/gui/plot3d/scene/interaction.py b/src/silx/gui/plot3d/scene/interaction.py index 14a54dc..91fab23 100644 --- a/src/silx/gui/plot3d/scene/interaction.py +++ b/src/silx/gui/plot3d/scene/interaction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2019 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides interaction to plug on the scene graph.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "25/07/2016" diff --git a/src/silx/gui/plot3d/scene/primitives.py b/src/silx/gui/plot3d/scene/primitives.py index 7f35c3c..6d3c4ff 100644 --- a/src/silx/gui/plot3d/scene/primitives.py +++ b/src/silx/gui/plot3d/scene/primitives.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2021 European Synchrotron Radiation Facility @@ -23,8 +22,6 @@ # # ###########################################################################*/ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/scene/test/__init__.py b/src/silx/gui/plot3d/scene/test/__init__.py index 3bb978e..4bdcc18 100644 --- a/src/silx/gui/plot3d/scene/test/__init__.py +++ b/src/silx/gui/plot3d/scene/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/scene/test/test_transform.py b/src/silx/gui/plot3d/scene/test/test_transform.py index 69e991b..2998c65 100644 --- a/src/silx/gui/plot3d/scene/test/test_transform.py +++ b/src/silx/gui/plot3d/scene/test/test_transform.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility @@ -23,8 +22,6 @@ # # ###########################################################################*/ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "05/01/2017" diff --git a/src/silx/gui/plot3d/scene/test/test_utils.py b/src/silx/gui/plot3d/scene/test/test_utils.py index 65d0ce0..a9ba6bc 100644 --- a/src/silx/gui/plot3d/scene/test/test_utils.py +++ b/src/silx/gui/plot3d/scene/test/test_utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility @@ -23,8 +22,6 @@ # # ###########################################################################*/ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/01/2018" diff --git a/src/silx/gui/plot3d/scene/text.py b/src/silx/gui/plot3d/scene/text.py index bacc2e6..3c4e692 100644 --- a/src/silx/gui/plot3d/scene/text.py +++ b/src/silx/gui/plot3d/scene/text.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Primitive displaying a text field in the scene.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/scene/transform.py b/src/silx/gui/plot3d/scene/transform.py index 43b739b..5c2cbb3 100644 --- a/src/silx/gui/plot3d/scene/transform.py +++ b/src/silx/gui/plot3d/scene/transform.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2020 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides 4x4 matrix operation and classes to handle them.""" -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "25/07/2016" diff --git a/src/silx/gui/plot3d/scene/utils.py b/src/silx/gui/plot3d/scene/utils.py index c6cd129..48fc2f5 100644 --- a/src/silx/gui/plot3d/scene/utils.py +++ b/src/silx/gui/plot3d/scene/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2020 European Synchrotron Radiation Facility @@ -27,8 +26,6 @@ This module provides functions to generate indices, to check intersection and to handle planes. """ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "25/07/2016" diff --git a/src/silx/gui/plot3d/scene/viewport.py b/src/silx/gui/plot3d/scene/viewport.py index 6de640e..bff77e2 100644 --- a/src/silx/gui/plot3d/scene/viewport.py +++ b/src/silx/gui/plot3d/scene/viewport.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2019 European Synchrotron Radiation Facility @@ -29,8 +28,6 @@ The attribute :attr:`scene` is the root group of the scene tree. :class:`RenderContext` handles the current state during rendering. """ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/scene/window.py b/src/silx/gui/plot3d/scene/window.py index b92c404..c8f4cee 100644 --- a/src/silx/gui/plot3d/scene/window.py +++ b/src/silx/gui/plot3d/scene/window.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility @@ -32,8 +31,6 @@ The :class:`Context` and :class:`ContextGL2` represent the operating system OpenGL context and handle OpenGL resources. """ -from __future__ import absolute_import, division, unicode_literals - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "10/01/2017" diff --git a/src/silx/gui/plot3d/setup.py b/src/silx/gui/plot3d/setup.py deleted file mode 100644 index 59c0230..0000000 --- a/src/silx/gui/plot3d/setup.py +++ /dev/null @@ -1,50 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2015-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. -# -# ###########################################################################*/ -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "25/07/2016" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('plot3d', parent_package, top_path) - config.add_subpackage('_model') - config.add_subpackage('actions') - config.add_subpackage('items') - config.add_subpackage('scene') - config.add_subpackage('scene.test') - config.add_subpackage('tools') - config.add_subpackage('tools.test') - config.add_subpackage('test') - config.add_subpackage('utils') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/gui/plot3d/test/__init__.py b/src/silx/gui/plot3d/test/__init__.py index 83491ad..f8afa83 100644 --- a/src/silx/gui/plot3d/test/__init__.py +++ b/src/silx/gui/plot3d/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/test/testGL.py b/src/silx/gui/plot3d/test/testGL.py index a7309a9..d1d53ef 100644 --- a/src/silx/gui/plot3d/test/testGL.py +++ b/src/silx/gui/plot3d/test/testGL.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility @@ -29,7 +28,7 @@ __date__ = "10/08/2017" import logging -import unittest +import pytest from silx.gui._glutils import gl, OpenGLWidget from silx.gui.utils.testutils import TestCaseQt @@ -39,6 +38,7 @@ from silx.gui import qt _logger = logging.getLogger(__name__) +@pytest.mark.usefixtures("use_opengl") class TestOpenGL(TestCaseQt): """Tests of OpenGL widget.""" diff --git a/src/silx/gui/plot3d/test/testScalarFieldView.py b/src/silx/gui/plot3d/test/testScalarFieldView.py index e6535fc..1e06e3f 100644 --- a/src/silx/gui/plot3d/test/testScalarFieldView.py +++ b/src/silx/gui/plot3d/test/testScalarFieldView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility @@ -29,7 +28,7 @@ __date__ = "17/01/2018" import logging -import unittest +import pytest import numpy @@ -44,6 +43,7 @@ from silx.gui.plot3d.SFViewParamTree import TreeView _logger = logging.getLogger(__name__) +@pytest.mark.usefixtures("use_opengl") class TestScalarFieldView(TestCaseQt, ParametricTestCase): """Tests of ScalarFieldView widget.""" diff --git a/src/silx/gui/plot3d/test/testSceneWidget.py b/src/silx/gui/plot3d/test/testSceneWidget.py index fc96781..e7f3b3f 100644 --- a/src/silx/gui/plot3d/test/testSceneWidget.py +++ b/src/silx/gui/plot3d/test/testSceneWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -28,7 +27,7 @@ __license__ = "MIT" __date__ = "06/03/2019" -import unittest +import pytest import numpy @@ -39,6 +38,7 @@ from silx.gui import qt from silx.gui.plot3d.SceneWidget import SceneWidget +@pytest.mark.usefixtures("use_opengl") class TestSceneWidget(TestCaseQt, ParametricTestCase): """Tests SceneWidget picking feature""" diff --git a/src/silx/gui/plot3d/test/testSceneWidgetPicking.py b/src/silx/gui/plot3d/test/testSceneWidgetPicking.py index d4d8db7..c0ad3b0 100644 --- a/src/silx/gui/plot3d/test/testSceneWidgetPicking.py +++ b/src/silx/gui/plot3d/test/testSceneWidgetPicking.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility @@ -28,7 +27,7 @@ __license__ = "MIT" __date__ = "03/10/2018" -import unittest +import pytest import numpy @@ -39,6 +38,7 @@ from silx.gui import qt from silx.gui.plot3d.SceneWidget import SceneWidget, items +@pytest.mark.usefixtures("use_opengl") class TestSceneWidgetPicking(TestCaseQt, ParametricTestCase): """Tests SceneWidget picking feature""" diff --git a/src/silx/gui/plot3d/test/testSceneWindow.py b/src/silx/gui/plot3d/test/testSceneWindow.py index 6b61335..09e097c 100644 --- a/src/silx/gui/plot3d/test/testSceneWindow.py +++ b/src/silx/gui/plot3d/test/testSceneWindow.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019-2021 European Synchrotron Radiation Facility @@ -28,7 +27,7 @@ __license__ = "MIT" __date__ = "22/03/2019" -import unittest +import pytest import numpy @@ -39,6 +38,8 @@ from silx.gui import qt from silx.gui.plot3d.SceneWindow import SceneWindow from silx.gui.plot3d.items import HeightMapData, HeightMapRGBA + +@pytest.mark.usefixtures("use_opengl") class TestSceneWindow(TestCaseQt, ParametricTestCase): """Tests SceneWidget picking feature""" diff --git a/src/silx/gui/plot3d/test/testStatsWidget.py b/src/silx/gui/plot3d/test/testStatsWidget.py index d452eb5..e1411bf 100644 --- a/src/silx/gui/plot3d/test/testStatsWidget.py +++ b/src/silx/gui/plot3d/test/testStatsWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -28,7 +27,7 @@ __license__ = "MIT" __date__ = "25/01/2019" -import unittest +import pytest import numpy @@ -43,6 +42,7 @@ from silx.gui.plot3d.ScalarFieldView import ScalarFieldView from silx.gui.plot3d.SceneWidget import SceneWidget, items +@pytest.mark.usefixtures("use_opengl") class TestSceneWidget(TestCaseQt, ParametricTestCase): """Tests StatsWidget combined with SceneWidget""" diff --git a/src/silx/gui/plot3d/tools/GroupPropertiesWidget.py b/src/silx/gui/plot3d/tools/GroupPropertiesWidget.py index 146c2cd..922df3a 100644 --- a/src/silx/gui/plot3d/tools/GroupPropertiesWidget.py +++ b/src/silx/gui/plot3d/tools/GroupPropertiesWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """:class:`GroupPropertiesWidget` allows to reset properties in a GroupItem.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "24/04/2018" diff --git a/src/silx/gui/plot3d/tools/PositionInfoWidget.py b/src/silx/gui/plot3d/tools/PositionInfoWidget.py index 99d6356..1998533 100644 --- a/src/silx/gui/plot3d/tools/PositionInfoWidget.py +++ b/src/silx/gui/plot3d/tools/PositionInfoWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a widget that displays data values of a SceneWidget. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "01/10/2018" diff --git a/src/silx/gui/plot3d/tools/ViewpointTools.py b/src/silx/gui/plot3d/tools/ViewpointTools.py index 0607382..ab26c96 100644 --- a/src/silx/gui/plot3d/tools/ViewpointTools.py +++ b/src/silx/gui/plot3d/tools/ViewpointTools.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """This module provides a toolbar to control Plot3DWidget viewpoint.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "08/09/2017" diff --git a/src/silx/gui/plot3d/tools/__init__.py b/src/silx/gui/plot3d/tools/__init__.py index c8b8d21..5e2c76c 100644 --- a/src/silx/gui/plot3d/tools/__init__.py +++ b/src/silx/gui/plot3d/tools/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/tools/test/__init__.py b/src/silx/gui/plot3d/tools/test/__init__.py index 86741ed..a6032b9 100644 --- a/src/silx/gui/plot3d/tools/test/__init__.py +++ b/src/silx/gui/plot3d/tools/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py b/src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py index 17fb3db..e988817 100644 --- a/src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py +++ b/src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/tools/toolbars.py b/src/silx/gui/plot3d/tools/toolbars.py index d4f32db..c89f6c6 100644 --- a/src/silx/gui/plot3d/tools/toolbars.py +++ b/src/silx/gui/plot3d/tools/toolbars.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -37,8 +36,6 @@ It provides the following toolbars: - Print """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "06/09/2017" diff --git a/src/silx/gui/plot3d/utils/__init__.py b/src/silx/gui/plot3d/utils/__init__.py index 99d3e08..3cf3825 100644 --- a/src/silx/gui/plot3d/utils/__init__.py +++ b/src/silx/gui/plot3d/utils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/plot3d/utils/mng.py b/src/silx/gui/plot3d/utils/mng.py index 8049a2f..52f619f 100644 --- a/src/silx/gui/plot3d/utils/mng.py +++ b/src/silx/gui/plot3d/utils/mng.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility @@ -28,8 +27,6 @@ It only supports RGB888 images of the same shape stored as MNG-VLC (very low complexity) format. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "15/12/2016" diff --git a/src/silx/gui/printer.py b/src/silx/gui/printer.py index 761fa0f..c0af97f 100644 --- a/src/silx/gui/printer.py +++ b/src/silx/gui/printer.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a singleton QPrinter used by default by silx widgets. """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "01/03/2018" diff --git a/src/silx/gui/qt/__init__.py b/src/silx/gui/qt/__init__.py index 915c89b..bc75041 100644 --- a/src/silx/gui/qt/__init__.py +++ b/src/silx/gui/qt/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -25,11 +24,12 @@ """Common wrapper over Python Qt bindings: - `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`_ -- `PySide2 <https://pypi.org/project/PySide2/>`_ - `PySide6 <https://pypi.org/project/PySide6/>`_ +- `PySide2 <https://pypi.org/project/PySide2/>`_ +- `PyQt6 <https://pypi.org/project/PyQt6/>`_ If a Qt binding is already loaded, it will use it, otherwise the different -Qt bindings are tried in this order: PyQt5, PySide2, PySide6. +Qt bindings are tried in this order: PyQt5, PySide6, PySide2, PyQt6. The name of the loaded Qt binding is stored in the BINDING variable. diff --git a/src/silx/gui/qt/_pyqt6.py b/src/silx/gui/qt/_pyqt6.py new file mode 100644 index 0000000..15b49bb --- /dev/null +++ b/src/silx/gui/qt/_pyqt6.py @@ -0,0 +1,64 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2021 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. +# +# ###########################################################################*/ +"""PyQt6 backward compatibility patching""" + +__authors__ = ["Thomas VINCENT"] +__license__ = "MIT" +__date__ = "02/09/2021" + +import enum +import logging + +import PyQt6.sip +from PyQt6.QtCore import Qt + + +_logger = logging.getLogger(__name__) + + +def patch_enums(*modules): + """Patch PyQt6 modules to provide backward compatibility of enum values + + :param modules: Modules to patch (e.g., PyQt6.QtCore). + """ + for module in modules: + for clsName in dir(module): + cls = getattr(module, clsName, None) + if isinstance(cls, PyQt6.sip.wrappertype) and clsName.startswith('Q'): + for qenumName in dir(cls): + if qenumName[0].isupper(): + qenum = getattr(cls, qenumName, None) + if isinstance(qenum, enum.EnumMeta): + if qenum is getattr(cls.__mro__[1], qenumName, None): + continue # Only handle it once + for item in qenum: + # Special cases to avoid overrides and mimic PySide6 + if clsName == 'QColorSpace' and qenumName in ( + 'Primaries', 'TransferFunction'): + break + if qenumName in ('DeviceType', 'PointerType'): + break + + setattr(cls, item.name, item) diff --git a/src/silx/gui/qt/_pyside_dynamic.py b/src/silx/gui/qt/_pyside_dynamic.py index a841eae..80520ac 100644 --- a/src/silx/gui/qt/_pyside_dynamic.py +++ b/src/silx/gui/qt/_pyside_dynamic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Taken from: https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 # Plus: https://github.com/spyder-ide/qtpy/commit/001a862c401d757feb63025f88dbb4601d353c84 diff --git a/src/silx/gui/qt/_qt.py b/src/silx/gui/qt/_qt.py index f62f4c8..b92fce2 100644 --- a/src/silx/gui/qt/_qt.py +++ b/src/silx/gui/qt/_qt.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2021 European Synchrotron Radiation Facility +# Copyright (c) 2004-2022 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 @@ -26,22 +25,23 @@ __authors__ = ["V.A. Sole"] __license__ = "MIT" -__date__ = "23/05/2018" +__date__ = "12/01/2022" import logging import sys import traceback +from silx.utils import deprecation _logger = logging.getLogger(__name__) BINDING = None -"""The name of the Qt binding in use: PyQt5, PySide2, PySide6.""" +"""The name of the Qt binding in use: PyQt5, PySide2, PySide6, PyQt6.""" QtBinding = None # noqa -"""The Qt binding module in use: PyQt5, PySide2, PySide6.""" +"""The Qt binding module in use: PyQt5, PySide2, PySide6, PyQt6.""" HAS_SVG = False """True if Qt provides support for Scalable Vector Graphics (QtSVG).""" @@ -50,7 +50,7 @@ HAS_OPENGL = False """True if Qt provides support for OpenGL (QtOpenGL).""" # First check for an already loaded wrapper -for _binding in ('PySide2', 'PyQt5', 'PySide6'): +for _binding in ('PySide2', 'PyQt5', 'PySide6', 'PyQt6'): if _binding + '.QtCore' in sys.modules: BINDING = _binding break @@ -61,27 +61,40 @@ else: # Then try Qt bindings if 'PyQt5' in sys.modules: del sys.modules["PyQt5"] try: - import PySide2.QtCore # noqa + import PySide6.QtCore # noqa except ImportError: - if 'PySide2' in sys.modules: - del sys.modules["PySide2"] + if 'PySide6' in sys.modules: + del sys.modules["PySide6"] try: - import PySide6.QtCore # noqa + import PySide2.QtCore # noqa except ImportError: - if 'PySide6' in sys.modules: - del sys.modules["PySide6"] - raise ImportError( - 'No Qt wrapper found. Install PyQt5, PySide2, PySide6.') + if 'PySide2' in sys.modules: + del sys.modules["PySide2"] + try: + import PyQt6.QtCore # noqa + except ImportError: + if 'PyQt6' in sys.modules: + del sys.modules["PyQt6"] + + raise ImportError( + 'No Qt wrapper found. Install PyQt5, PySide2, PySide6, PyQt6.') + else: + BINDING = 'PyQt6' else: - BINDING = 'PySide6' + BINDING = 'PySide2' else: - BINDING = 'PySide2' + BINDING = 'PySide6' else: BINDING = 'PyQt5' if BINDING == 'PyQt5': _logger.debug('Using PyQt5 bindings') + from PyQt5 import QtCore + if sys.version_info >= (3, 10) and QtCore.PYQT_VERSION < 0x50e02: + raise RuntimeError( + "PyQt5 v%s is not supported, please upgrade it." % QtCore.PYQT_VERSION_STR + ) import PyQt5 as QtBinding # noqa @@ -121,7 +134,12 @@ if BINDING == 'PyQt5': elif BINDING == 'PySide2': - _logger.debug('Using PySide2 bindings') + deprecation.deprecated_warning( + type_="Qt Binding", + name="PySide2", + replacement="PySide6", + since_version="1.1", + ) import PySide2 as QtBinding # noqa @@ -156,7 +174,7 @@ elif BINDING == 'PySide2': return super().exec_(*args, **kwargs) # QtWidgets - class QApplication(_ExecMixIn, QApplication): pass + QApplication.exec = QApplication.exec_ class QColorDialog(_ExecMixIn, QColorDialog): pass class QDialog(_ExecMixIn, QDialog): pass class QErrorMessage(_ExecMixIn, QErrorMessage): pass @@ -189,7 +207,7 @@ elif BINDING == 'PySide6': from PySide6.QtOpenGL import * # noqa from PySide6.QtOpenGLWidgets import QOpenGLWidget # noqa except ImportError: - _logger.info("PySide6.QtOpenGL not available") + _logger.info("PySide6's QtOpenGL or QtOpenGLWidgets not available") HAS_OPENGL = False else: HAS_OPENGL = True @@ -204,8 +222,63 @@ elif BINDING == 'PySide6': pyqtSignal = Signal + +elif BINDING == 'PyQt6': + _logger.debug('Using PyQt6 bindings') + + # Monkey-patch module to expose enum values for compatibility + # All Qt modules loaded here should be patched. + from . import _pyqt6 + from PyQt6 import QtCore + if QtCore.PYQT_VERSION < int("0x60300", 16): + raise RuntimeError( + "PyQt6 v%s is not supported, please upgrade it." % QtCore.PYQT_VERSION_STR + ) + + from PyQt6 import QtGui, QtWidgets, QtPrintSupport, QtOpenGL, QtSvg + from PyQt6 import QtTest as _QtTest + _pyqt6.patch_enums( + QtCore, QtGui, QtWidgets, QtPrintSupport, QtOpenGL, QtSvg, _QtTest) + + import PyQt6 as QtBinding # noqa + + from PyQt6.QtCore import * # noqa + from PyQt6.QtGui import * # noqa + from PyQt6.QtWidgets import * # noqa + from PyQt6.QtPrintSupport import * # noqa + + try: + from PyQt6.QtOpenGL import * # noqa + from PyQt6.QtOpenGLWidgets import QOpenGLWidget # noqa + except ImportError: + _logger.info("PyQt6's QtOpenGL or QtOpenGLWidgets not available") + HAS_OPENGL = False + else: + HAS_OPENGL = True + + try: + from PyQt6.QtSvg import * # noqa + except ImportError: + _logger.info("PyQt6.QtSvg not available") + HAS_SVG = False + else: + HAS_SVG = True + + from PyQt6.uic import loadUi # noqa + + Signal = pyqtSignal + + Property = pyqtProperty + + Slot = pyqtSlot + + # Disable PyQt6 cooperative multi-inheritance since other bindings do not provide it. + # See https://www.riverbankcomputing.com/static/Docs/PyQt6/multiinheritance.html?highlight=inheritance + class _Foo(object): pass + class QObject(QObject, _Foo): pass + else: - raise ImportError('No Qt wrapper found. Install PyQt5, PySide2 or PySide6') + raise ImportError('No Qt wrapper found. Install PyQt5, PySide2, PySide6 or PyQt6') # provide a exception handler but not implement it by default diff --git a/src/silx/gui/qt/_utils.py b/src/silx/gui/qt/_utils.py index 5dced95..fb2b8ce 100644 --- a/src/silx/gui/qt/_utils.py +++ b/src/silx/gui/qt/_utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -33,6 +32,19 @@ __date__ = "30/11/2016" from . import _qt +def getMouseEventPosition(event): + """Qt5/Qt6 compatibility wrapper to access QMouseEvent position + + :param QMouseEvent event: + :returns: (x, y) as a tuple of float + """ + if _qt.BINDING in ("PyQt5", "PySide2"): + return float(event.x()), float(event.y()) + # Qt6 + position = event.position() + return position.x(), position.y() + + def supportedImageFormats(): """Return a set of string of file format extensions supported by the Qt runtime.""" diff --git a/src/silx/gui/qt/inspect.py b/src/silx/gui/qt/inspect.py index b9a0d1d..c7fe32a 100644 --- a/src/silx/gui/qt/inspect.py +++ b/src/silx/gui/qt/inspect.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -69,6 +68,21 @@ elif qt.BINDING == 'PySide2': elif qt.BINDING == 'PySide6': from shiboken6 import isValid, createdByPython, ownedByPython # noqa +elif qt.BINDING == 'PyQt6': + from PyQt6.sip import isdeleted as _isdeleted # noqa + from PyQt6.sip import ispycreated as createdByPython # noqa + from PyQt6.sip import ispyowned as ownedByPython # noqa + + def isValid(obj): + """Returns True if underlying C++ object is valid. + + :param QObject obj: + :rtype: bool + """ + return not _isdeleted(obj) + + + else: raise ImportError("Unsupported Qt binding %s" % qt.BINDING) diff --git a/src/silx/gui/setup.py b/src/silx/gui/setup.py deleted file mode 100644 index 04a2bac..0000000 --- a/src/silx/gui/setup.py +++ /dev/null @@ -1,55 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "28/11/2017" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('gui', parent_package, top_path) - config.add_subpackage('_glutils') - config.add_subpackage('qt') - config.add_subpackage('plot') - config.add_subpackage('fit') - config.add_subpackage('hdf5') - config.add_subpackage('widgets') - config.add_subpackage('test') - config.add_subpackage('plot3d') - config.add_subpackage('data') - config.add_subpackage('dialog') - config.add_subpackage('utils') - config.add_subpackage('utils.glutils') - config.add_subpackage('utils.test') - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/gui/test/__init__.py b/src/silx/gui/test/__init__.py index 00d6216..d9e06fc 100644 --- a/src/silx/gui/test/__init__.py +++ b/src/silx/gui/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/test/test_colors.py b/src/silx/gui/test/test_colors.py index fa87d7d..b0e6139 100755 --- a/src/silx/gui/test/test_colors.py +++ b/src/silx/gui/test/test_colors.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides the Colormap object """ -from __future__ import absolute_import - __authors__ = ["H.Payno"] __license__ = "MIT" __date__ = "09/11/2018" diff --git a/src/silx/gui/test/test_console.py b/src/silx/gui/test/test_console.py index 21f3564..f636287 100644 --- a/src/silx/gui/test/test_console.py +++ b/src/silx/gui/test/test_console.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Basic tests for IPython console widget""" -from __future__ import print_function - __authors__ = ["P. Knobel"] __license__ = "MIT" __date__ = "05/12/2016" diff --git a/src/silx/gui/test/test_icons.py b/src/silx/gui/test/test_icons.py index 154adf6..59c7e00 100644 --- a/src/silx/gui/test/test_icons.py +++ b/src/silx/gui/test/test_icons.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/test/test_qt.py b/src/silx/gui/test/test_qt.py index 8554744..692d7f7 100644 --- a/src/silx/gui/test/test_qt.py +++ b/src/silx/gui/test/test_qt.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/test/utils.py b/src/silx/gui/test/utils.py index db4c0ee..1cfee67 100644 --- a/src/silx/gui/test/utils.py +++ b/src/silx/gui/test/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Color conversion function, color dictionary and colormap tools.""" -from __future__ import absolute_import - __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "05/10/2018" diff --git a/src/silx/gui/utils/__init__.py b/src/silx/gui/utils/__init__.py index 726ad74..4fae646 100755 --- a/src/silx/gui/utils/__init__.py +++ b/src/silx/gui/utils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/utils/concurrent.py b/src/silx/gui/utils/concurrent.py index c27374f..242e804 100644 --- a/src/silx/gui/utils/concurrent.py +++ b/src/silx/gui/utils/concurrent.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module allows to run a function in Qt main thread from another thread """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "09/03/2018" diff --git a/src/silx/gui/utils/glutils/__init__.py b/src/silx/gui/utils/glutils/__init__.py index 20e611e..2651402 100644 --- a/src/silx/gui/utils/glutils/__init__.py +++ b/src/silx/gui/utils/glutils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020-2021 European Synchrotron Radiation Facility @@ -52,14 +51,15 @@ class _isOpenGLAvailableResult: return '<_isOpenGLAvailableResult: %s, "%s">' % (self.status, self.error) -def _runtimeOpenGLCheck(version): +def _runtimeOpenGLCheck(version, shareOpenGLContexts): """Run OpenGL check in a subprocess. This is done by starting a subprocess that displays a Qt OpenGL widget. :param List[int] version: The minimal required OpenGL version as a 2-tuple (major, minor). - Default: (2, 1) + :param bool shareOpenGLContexts: + True to test the `QApplication` with `AA_ShareOpenGLContexts`. :return: An error string that is empty if no error occured :rtype: str """ @@ -69,10 +69,10 @@ def _runtimeOpenGLCheck(version): [os.path.abspath(p) for p in sys.path]) try: - error = subprocess.check_output( - [sys.executable, '-s', '-S', __file__, major, minor], - env=env, - timeout=2) + cmd = [sys.executable, '-s', '-S', __file__, major, minor] + if shareOpenGLContexts: + cmd.append("--shareOpenGLContexts") + error = subprocess.check_output(cmd, env=env, timeout=2) except subprocess.TimeoutExpired: status = False error = "Qt OpenGL widget hang" @@ -90,7 +90,7 @@ def _runtimeOpenGLCheck(version): _runtimeCheckCache = {} # Cache runtime check results: {version: result} -def isOpenGLAvailable(version=(2, 1), runtimeCheck=True): +def isOpenGLAvailable(version=(2, 1), runtimeCheck=True, shareOpenGLContexts=False): """Check if OpenGL is available through Qt and actually working. After some basic tests, this is done by starting a subprocess that @@ -99,8 +99,12 @@ def isOpenGLAvailable(version=(2, 1), runtimeCheck=True): :param List[int] version: The minimal required OpenGL version as a 2-tuple (major, minor). Default: (2, 1) + :param bool shareOpenGLContexts: + True to test the `QApplication` with `AA_ShareOpenGLContexts`. + This only can be checked with `runtimeCheck` enabled. + Default is false. :param bool runtimeCheck: - True (default) to run the test creating a Qt OpenGL widgt in a subprocess, + True (default) to run the test creating a Qt OpenGL widget in a subprocess, False to avoid this check. :return: A result object that evaluates to True if successful and which has a `status` boolean attribute (True if successful) and @@ -131,12 +135,13 @@ def isOpenGLAvailable(version=(2, 1), runtimeCheck=True): result = _isOpenGLAvailableResult(error == '', error) + keyCache = version, shareOpenGLContexts if result: # No error so far, runtime check - if version in _runtimeCheckCache: # Use cache - result = _runtimeCheckCache[version] + if keyCache in _runtimeCheckCache: # Use cache + result = _runtimeCheckCache[keyCache] elif runtimeCheck: # Run test in subprocess - result = _runtimeOpenGLCheck(version) - _runtimeCheckCache[version] = result + result = _runtimeOpenGLCheck(version, shareOpenGLContexts) + _runtimeCheckCache[keyCache] = result return result @@ -178,9 +183,12 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('major') parser.add_argument('minor') + parser.add_argument('--shareOpenGLContexts', action="store_true") args = parser.parse_args(args=sys.argv[1:]) + if args.shareOpenGLContexts: + qt.QCoreApplication.setAttribute(qt.Qt.AA_ShareOpenGLContexts) app = qt.QApplication([]) window = qt.QMainWindow(flags= qt.Qt.Popup | diff --git a/src/silx/gui/utils/image.py b/src/silx/gui/utils/image.py index 96f50ab..1757e3e 100644 --- a/src/silx/gui/utils/image.py +++ b/src/silx/gui/utils/image.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -28,9 +27,6 @@ - :func:`convertQImageToArray` """ -from __future__ import division - - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "04/09/2018" @@ -118,6 +114,8 @@ def convertQImageToArray(image): ptr = image.bits() if qt.BINDING == 'PyQt5': ptr.setsize(image.byteCount()) + elif qt.BINDING == 'PyQt6': + ptr.setsize(image.sizeInBytes()) elif qt.BINDING in ('PySide2', 'PySide6'): ptr = ptr.tobytes() else: diff --git a/src/silx/gui/utils/matplotlib.py b/src/silx/gui/utils/matplotlib.py index 90257f8..277a303 100644 --- a/src/silx/gui/utils/matplotlib.py +++ b/src/silx/gui/utils/matplotlib.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -23,8 +22,6 @@ # # ###########################################################################*/ -from __future__ import absolute_import - """This module initializes matplotlib and sets-up the backend to use. It MUST be imported prior to any other import of matplotlib. @@ -38,27 +35,116 @@ __license__ = "MIT" __date__ = "02/05/2018" +import io from pkg_resources import parse_version import matplotlib +import numpy from .. import qt +def rasterMathText(text, font, size=-1, weight=-1, italic=False, devicePixelRatio=1.0): + """Raster text using matplotlib supporting latex-like math syntax. + + It supports multiple lines. + + :param str text: The text to raster + :param font: Font name or QFont to use + :type font: str or :class:`QFont` + :param int size: + Font size in points + Used only if font is given as name. + :param int weight: + Font weight in [0, 99], see QFont.Weight. + Used only if font is given as name. + :param bool italic: + True for italic font (default: False). + Used only if font is given as name. + :param float devicePixelRatio: + The current ratio between device and device-independent pixel + (default: 1.0) + :return: Corresponding image in gray scale and baseline offset from top + :rtype: (HxW numpy.ndarray of uint8, int) + """ + # Implementation adapted from: + # https://github.com/matplotlib/matplotlib/blob/d624571a19aec7c7d4a24123643288fc27db17e7/lib/matplotlib/mathtext.py#L264 + + # Lazy import to avoid imports before setting matplotlib's rcParams + from matplotlib.font_manager import FontProperties + from matplotlib.mathtext import MathTextParser + from matplotlib import figure + + dpi = 96 # default + qapp = qt.QApplication.instance() + if qapp: + screen = qapp.primaryScreen() + if screen: + dpi = screen.logicalDotsPerInchY() + + # Make sure dpi is even, it causes issues with array reshape otherwise + dpi = ((dpi * devicePixelRatio) // 2) * 2 + + stripped_text = text.strip("\n") + + parser = MathTextParser("path") + width, height, depth, _, _ = parser.parse(stripped_text, dpi=dpi) + width *= 2 + height *= 2 * (stripped_text.count("\n") + 1) + + if not isinstance(font, qt.QFont): + font = qt.QFont(font, size, weight, italic) + prop = FontProperties( + family=font.family(), + style="italic" if font.italic() else "normal", + weight=10 * font.weight(), + size=font.pointSize(), + ) + + fig = figure.Figure(figsize=(width / dpi, height / dpi)) + fig.text(0, depth / height, stripped_text, fontproperties=prop) + with io.BytesIO() as buffer: + fig.savefig(buffer, dpi=dpi, format="raw") + buffer.seek(0) + image = numpy.frombuffer(buffer.read(), dtype=numpy.uint8).reshape( + int(height), int(width), 4 + ) + + # RGB to inverted R channel + array = 255 - image[:, :, 0] + + # Remove leading and trailing empty columns/rows but one on each side + filled_rows = numpy.nonzero(numpy.sum(array, axis=1))[0] + filled_columns = numpy.nonzero(numpy.sum(array, axis=0))[0] + if len(filled_rows) == 0 or len(filled_columns) == 0: + return array, image.shape[0] - 1 + + clipped_array = numpy.ascontiguousarray( + array[ + max(0, filled_rows[0] - 1) : filled_rows[-1] + 2, + max(0, filled_columns[0] - 1) : filled_columns[-1] + 2, + ] + ) + + return clipped_array, image.shape[0] - 1 # baseline not available + + def _matplotlib_use(backend, force): """Wrapper of `matplotlib.use` to set-up backend. - It adds extra initialization for PySide2 with matplotlib < 2.2. + It adds extra initialization for PySide2 with matplotlib < 2.2. """ # This is kept for compatibility with matplotlib < 2.2 - if (parse_version(matplotlib.__version__) < parse_version('2.2') and - qt.BINDING == 'PySide2'): - matplotlib.rcParams['backend.qt5'] = 'PySide2' + if ( + parse_version(matplotlib.__version__) < parse_version("2.2") + and qt.BINDING == "PySide2" + ): + matplotlib.rcParams["backend.qt5"] = "PySide2" matplotlib.use(backend, force=force) -if qt.BINDING in ('PySide6', 'PyQt5', 'PySide2'): - _matplotlib_use('Qt5Agg', force=False) +if qt.BINDING in ("PySide6", "PyQt6", "PyQt5", "PySide2"): + _matplotlib_use("Qt5Agg", force=False) from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg # noqa else: diff --git a/src/silx/gui/utils/projecturl.py b/src/silx/gui/utils/projecturl.py index 0832c2e..116017e 100644 --- a/src/silx/gui/utils/projecturl.py +++ b/src/silx/gui/utils/projecturl.py @@ -1,4 +1,3 @@ -# coding: utf-8 # # Project: Azimuthal integration # https://github.com/silx-kit/silx @@ -23,8 +22,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import absolute_import, print_function, division - """Provide convenient URL for silx-kit projects.""" __author__ = "Valentin Valls" diff --git a/src/silx/gui/utils/qtutils.py b/src/silx/gui/utils/qtutils.py index 9682913..d686a48 100755 --- a/src/silx/gui/utils/qtutils.py +++ b/src/silx/gui/utils/qtutils.py @@ -1,4 +1,3 @@ -# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2020 European Synchrotron Radiation Facility
diff --git a/src/silx/gui/utils/signal.py b/src/silx/gui/utils/signal.py index 359f5cc..cd376a9 100644 --- a/src/silx/gui/utils/signal.py +++ b/src/silx/gui/utils/signal.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2012 University of North Carolina at Chapel Hill, Luke Campagnola diff --git a/src/silx/gui/utils/test/__init__.py b/src/silx/gui/utils/test/__init__.py index 15cd186..7a8edb9 100755 --- a/src/silx/gui/utils/test/__init__.py +++ b/src/silx/gui/utils/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/utils/test/test.py b/src/silx/gui/utils/test/test.py index 0208d64..42bf5a2 100644 --- a/src/silx/gui/utils/test/test.py +++ b/src/silx/gui/utils/test/test.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019-2021 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Test of functions available in silx.gui.utils module.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "01/08/2019" diff --git a/src/silx/gui/utils/test/test_async.py b/src/silx/gui/utils/test/test_async.py index 7304ca9..1fd8509 100644 --- a/src/silx/gui/utils/test/test_async.py +++ b/src/silx/gui/utils/test/test_async.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Test of async module.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "09/03/2018" diff --git a/src/silx/gui/utils/test/test_glutils.py b/src/silx/gui/utils/test/test_glutils.py index 7c9831b..4921f16 100644 --- a/src/silx/gui/utils/test/test_glutils.py +++ b/src/silx/gui/utils/test/test_glutils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020 European Synchrotron Radiation Facility @@ -30,26 +29,24 @@ __date__ = "15/01/2020" import logging -import unittest +import pytest + from silx.gui.utils.glutils import isOpenGLAvailable _logger = logging.getLogger(__name__) -class TestIsOpenGLAvailable(unittest.TestCase): - """Test isOpenGLAvailable""" - - def test(self): - for version in ((2, 1), (2, 1), (1000, 1)): - with self.subTest(version=version): - result = isOpenGLAvailable(version=version) - _logger.info("isOpenGLAvailable returned: %s", str(result)) - if version[0] == 1000: - self.assertFalse(result) - if not result: - self.assertFalse(result.status) - self.assertTrue(len(result.error) > 0) - else: - self.assertTrue(result.status) - self.assertTrue(len(result.error) == 0) +@pytest.mark.parametrize("params", (((2, 1), False), ((2, 1), False), ((1000, 1), False), ((2, 1), True))) +def testOpenGLAvailable(params): + version, shareOpenGLContexts = params + result = isOpenGLAvailable(version=version, shareOpenGLContexts=shareOpenGLContexts) + _logger.info("isOpenGLAvailable returned: %s", str(result)) + if version[0] == 1000: + assert not result + if not result: + assert not result.status + assert len(result.error) > 0 + else: + assert result.status + assert len(result.error) == 0 diff --git a/src/silx/gui/utils/test/test_image.py b/src/silx/gui/utils/test/test_image.py index 62316b0..07bc396 100644 --- a/src/silx/gui/utils/test/test_image.py +++ b/src/silx/gui/utils/test/test_image.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/utils/test/test_qtutils.py b/src/silx/gui/utils/test/test_qtutils.py index c00280b..c5ff2d2 100755 --- a/src/silx/gui/utils/test/test_qtutils.py +++ b/src/silx/gui/utils/test/test_qtutils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Test of functions available in silx.gui.utils module.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "01/08/2019" diff --git a/src/silx/gui/utils/test/test_testutils.py b/src/silx/gui/utils/test/test_testutils.py index 07294a7..e8a0123 100644 --- a/src/silx/gui/utils/test/test_testutils.py +++ b/src/silx/gui/utils/test/test_testutils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2019 European Synchrotron Radiation Facility diff --git a/src/silx/gui/utils/testutils.py b/src/silx/gui/utils/testutils.py index 40c8237..1ec9b0b 100644 --- a/src/silx/gui/utils/testutils.py +++ b/src/silx/gui/utils/testutils.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -26,7 +25,7 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "05/10/2018" +__date__ = "22/07/2022" import gc @@ -49,6 +48,8 @@ elif qt.BINDING == 'PyQt5': from PyQt5.QtTest import QTest elif qt.BINDING == 'PySide6': from PySide6.QtTest import QTest +elif qt.BINDING == 'PyQt6': + from PyQt6.QtTest import QTest else: raise ImportError('Unsupported Qt bindings') @@ -140,10 +141,14 @@ class TestCaseQt(unittest.TestCase): def _currentTestSucceeded(self): if hasattr(self, '_outcome'): - # For Python >= 3.4 - result = self.defaultTestResult() # these 2 methods have no side effects - if hasattr(self._outcome, 'errors'): - self._feedErrorsToResult(result, self._outcome.errors) + if hasattr(self, '_feedErrorsToResult'): + # For Python 3.4 -3.10 + result = self.defaultTestResult() # these 2 methods have no side effects + if hasattr(self._outcome, 'errors'): + self._feedErrorsToResult(result, self._outcome.errors) + else: + # Python 3.11+ + result = self._outcome.result else: # For Python < 3.4 result = getattr(self, '_outcomeForDoCleanups', self._resultForDoCleanups) @@ -155,11 +160,11 @@ class TestCaseQt(unittest.TestCase): def _checkForUnreleasedWidgets(self): """Test fixture checking that no more widgets exists.""" - gc.collect() - if self.__previousWidgets is None: return # Do not test for leaking widgets with PySide2 + gc.collect() + widgets = [widget for widget in self.qapp.allWidgets() if (widget not in self.__previousWidgets and _inspect.createdByPython(widget))] @@ -253,7 +258,7 @@ class TestCaseQt(unittest.TestCase): See QTest.mouseClick for details. """ if modifier is None: - modifier = qt.Qt.KeyboardModifiers() + modifier = self.qapp.keyboardModifiers() pos = qt.QPoint(int(pos[0]), int(pos[1])) if pos is not None else qt.QPoint() QTest.mouseClick(widget, button, modifier, pos, delay) self.qWait(20) @@ -264,7 +269,7 @@ class TestCaseQt(unittest.TestCase): See QTest.mouseDClick for details. """ if modifier is None: - modifier = qt.Qt.KeyboardModifiers() + modifier = self.qapp.keyboardModifiers() pos = qt.QPoint(int(pos[0]), int(pos[1])) if pos is not None else qt.QPoint() QTest.mouseDClick(widget, button, modifier, pos, delay) self.qWait(20) @@ -284,7 +289,7 @@ class TestCaseQt(unittest.TestCase): See QTest.mousePress for details. """ if modifier is None: - modifier = qt.Qt.KeyboardModifiers() + modifier = self.qapp.keyboardModifiers() pos = qt.QPoint(int(pos[0]), int(pos[1])) if pos is not None else qt.QPoint() QTest.mousePress(widget, button, modifier, pos, delay) self.qWait(20) @@ -295,7 +300,7 @@ class TestCaseQt(unittest.TestCase): See QTest.mouseRelease for details. """ if modifier is None: - modifier = qt.Qt.KeyboardModifiers() + modifier = self.qapp.keyboardModifiers() pos = qt.QPoint(int(pos[0]), int(pos[1])) if pos is not None else qt.QPoint() QTest.mouseRelease(widget, button, modifier, pos, delay) self.qWait(20) @@ -486,7 +491,7 @@ def getQToolButtonFromAction(action): :param QAction action: The QAction from which to get QToolButton. :return: A QToolButton associated to action or None. """ - if qt.BINDING == "PySide6": + if qt.BINDING in ("PySide6", "PyQt6"): widgets = action.associatedObjects() else: widgets = action.associatedWidgets() diff --git a/src/silx/gui/widgets/BoxLayoutDockWidget.py b/src/silx/gui/widgets/BoxLayoutDockWidget.py index 3d2b853..aa45153 100644 --- a/src/silx/gui/widgets/BoxLayoutDockWidget.py +++ b/src/silx/gui/widgets/BoxLayoutDockWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/ColormapNameComboBox.py b/src/silx/gui/widgets/ColormapNameComboBox.py index fa8faf1..388b032 100644 --- a/src/silx/gui/widgets/ColormapNameComboBox.py +++ b/src/silx/gui/widgets/ColormapNameComboBox.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2018 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """A QComboBox to display prefered colormaps """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent", "H. Payno"] __license__ = "MIT" __date__ = "27/11/2018" diff --git a/src/silx/gui/widgets/ElidedLabel.py b/src/silx/gui/widgets/ElidedLabel.py index 7c6dfb5..3760ec0 100644 --- a/src/silx/gui/widgets/ElidedLabel.py +++ b/src/silx/gui/widgets/ElidedLabel.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -28,17 +27,18 @@ __license__ = "MIT" __date__ = "07/12/2018" +from ...utils.deprecation import deprecated from silx.gui import qt class ElidedLabel(qt.QLabel): - """QLabel with an edile property. + """QLabel with an elide property. - By default if the text is too big, it is elided on the right. + By default if the text is too long, it is elided on the right. This mode can be changed with :func:`setElideMode`. - In case the text is elided, the full content is displayed as part of the + In this case the text is elided, the full content is displayed as part of the tool tip. This behavior can be disabled with :func:`setTextAsToolTip`. """ @@ -83,25 +83,41 @@ class ElidedLabel(qt.QLabel): else: qt.QLabel.setToolTip(self, self.__toolTip) - # Properties + # Inherited properties + + def text(self): + """Returns the text defined by the user. + + It can be different from the one really displayed, depending on the + `elideMode` defined for this widget. + """ + return self.__text + + @deprecated(replacement='text', since_version='1.1.0') + def getText(self): + return self.text() def setText(self, text): self.__text = text self.__updateText() - def getText(self): - return self.__text + def toolTip(self): + """Returns the tooltip defined by the user. - text = qt.Property(str, getText, setText) + It can be different from the one really displayed, if `textAsToolTip` was + set to true. + """ + return self.__toolTip + + @deprecated(replacement='toolTip', since_version='1.1.0') + def getToolTip(self): + return self.toolTip() def setToolTip(self, toolTip): self.__toolTip = toolTip self.__updateToolTip() - def getToolTip(self): - return self.__toolTip - - toolTip = qt.Property(str, getToolTip, setToolTip) + # New properties def setElideMode(self, elideMode): """Set the elide mode. @@ -118,7 +134,7 @@ class ElidedLabel(qt.QLabel): """ return self.__elideMode - elideMode = qt.Property(qt.Qt.TextElideMode, getToolTip, setToolTip) + elideMode = qt.Property(qt.Qt.TextElideMode, getElideMode, setElideMode) def setTextAsToolTip(self, enabled): """Enable displaying text as part of the tooltip if it is elided. diff --git a/src/silx/gui/widgets/FloatEdit.py b/src/silx/gui/widgets/FloatEdit.py index 08ed67d..61f518f 100644 --- a/src/silx/gui/widgets/FloatEdit.py +++ b/src/silx/gui/widgets/FloatEdit.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """Module contains a float editor """ -from __future__ import division - __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" __date__ = "02/10/2017" diff --git a/src/silx/gui/widgets/FlowLayout.py b/src/silx/gui/widgets/FlowLayout.py index 3c4c9dd..917aa09 100644 --- a/src/silx/gui/widgets/FlowLayout.py +++ b/src/silx/gui/widgets/FlowLayout.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ """This module provides a flow layout for QWidget: :class:`FlowLayout`. """ -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "20/07/2018" diff --git a/src/silx/gui/widgets/FormGridLayout.py b/src/silx/gui/widgets/FormGridLayout.py new file mode 100644 index 0000000..6068d30 --- /dev/null +++ b/src/silx/gui/widgets/FormGridLayout.py @@ -0,0 +1,74 @@ +# /*########################################################################## +# +# Copyright (c) 2022 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. +# +# ###########################################################################*/ +"""This module provides a form layout for QWidget: :class:`FormGridLayout`. +""" + +__authors__ = ["V. Valls"] +__license__ = "MIT" +__date__ = "29/09/2022" + + +import typing +from .. import qt + + +class FormGridLayout(qt.QGridLayout): + """A layout with the API of :class:`qt.QFormLayout` based on a :class:`qt.QGridLayout`. + + This allow a bit more flexibility, like allow vertical expanding + of the rows. + """ + def __init__(self, parent): + super(FormGridLayout, self).__init__(parent) + self.__cursor = 0 + + def _addCell(self, something, row, column, rowSpan=1, columnSpan=1): + if isinstance(something, qt.QLayout): + self.addLayout(something, row, column, rowSpan, columnSpan) + else: + if isinstance(something, str): + something = qt.QLabel(something) + self.addWidget(something, row, column, rowSpan, columnSpan) + + def addRow(self, label: typing.Union[str, qt.QWidget, qt.QLayout], field: typing.Union[None, qt.QWidget, qt.QLayout] = None): + """ + Adds a new row to the bottom of this form layout. + + If field is defined, the given label and field are added. + + Else, the label is a widget and spans both columns. + """ + if field is None: + self._addCell(label, self.__cursor, 0, 1, 2) + else: + self._addCell(label, self.__cursor, 0) + self._addCell(field, self.__cursor, 1) + self.__cursor += 1 + + def addItem(self, item: qt.QLayoutItem): + """ + Adds a new layout item to the bottom of this form layout. + """ + super(FormGridLayout, self).addItem(item) + self.__cursor += 1 diff --git a/src/silx/gui/widgets/FrameBrowser.py b/src/silx/gui/widgets/FrameBrowser.py index 671991f..17a9148 100644 --- a/src/silx/gui/widgets/FrameBrowser.py +++ b/src/silx/gui/widgets/FrameBrowser.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/HierarchicalTableView.py b/src/silx/gui/widgets/HierarchicalTableView.py index 3ccf4c7..6e6329b 100644 --- a/src/silx/gui/widgets/HierarchicalTableView.py +++ b/src/silx/gui/widgets/HierarchicalTableView.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/LegendIconWidget.py b/src/silx/gui/widgets/LegendIconWidget.py index 1c95e41..d0d2f5c 100755 --- a/src/silx/gui/widgets/LegendIconWidget.py +++ b/src/silx/gui/widgets/LegendIconWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/MedianFilterDialog.py b/src/silx/gui/widgets/MedianFilterDialog.py index dd4a00d..982736c 100644 --- a/src/silx/gui/widgets/MedianFilterDialog.py +++ b/src/silx/gui/widgets/MedianFilterDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/MultiModeAction.py b/src/silx/gui/widgets/MultiModeAction.py index 502275d..b40d285 100644 --- a/src/silx/gui/widgets/MultiModeAction.py +++ b/src/silx/gui/widgets/MultiModeAction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/PeriodicTable.py b/src/silx/gui/widgets/PeriodicTable.py index 6fed109..1fc3bab 100644 --- a/src/silx/gui/widgets/PeriodicTable.py +++ b/src/silx/gui/widgets/PeriodicTable.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/PrintGeometryDialog.py b/src/silx/gui/widgets/PrintGeometryDialog.py index 98ff8d1..db905fb 100644 --- a/src/silx/gui/widgets/PrintGeometryDialog.py +++ b/src/silx/gui/widgets/PrintGeometryDialog.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/PrintPreview.py b/src/silx/gui/widgets/PrintPreview.py index 53e0a1f..dd6af1f 100644 --- a/src/silx/gui/widgets/PrintPreview.py +++ b/src/silx/gui/widgets/PrintPreview.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/RangeSlider.py b/src/silx/gui/widgets/RangeSlider.py index 61b73fc..4db0470 100644 --- a/src/silx/gui/widgets/RangeSlider.py +++ b/src/silx/gui/widgets/RangeSlider.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2021 European Synchrotron Radiation Facility @@ -27,7 +26,6 @@ .. image:: img/RangeSlider.png :align: center """ -from __future__ import absolute_import, division __authors__ = ["D. Naudet", "T. Vincent"] __license__ = "MIT" diff --git a/src/silx/gui/widgets/TableWidget.py b/src/silx/gui/widgets/TableWidget.py index 50eb9e2..9bada5e 100644 --- a/src/silx/gui/widgets/TableWidget.py +++ b/src/silx/gui/widgets/TableWidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/ThreadPoolPushButton.py b/src/silx/gui/widgets/ThreadPoolPushButton.py index 949b6ef..8a1d428 100644 --- a/src/silx/gui/widgets/ThreadPoolPushButton.py +++ b/src/silx/gui/widgets/ThreadPoolPushButton.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/WaitingPushButton.py b/src/silx/gui/widgets/WaitingPushButton.py index 443dc9a..8bd9ea0 100644 --- a/src/silx/gui/widgets/WaitingPushButton.py +++ b/src/silx/gui/widgets/WaitingPushButton.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/__init__.py b/src/silx/gui/widgets/__init__.py index 9d0299d..cab7ef6 100644 --- a/src/silx/gui/widgets/__init__.py +++ b/src/silx/gui/widgets/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/setup.py b/src/silx/gui/widgets/setup.py deleted file mode 100644 index e96ac8d..0000000 --- a/src/silx/gui/widgets/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "11/10/2016" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('widgets', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/gui/widgets/test/__init__.py b/src/silx/gui/widgets/test/__init__.py index 243dbc7..03af6f2 100644 --- a/src/silx/gui/widgets/test/__init__.py +++ b/src/silx/gui/widgets/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_boxlayoutdockwidget.py b/src/silx/gui/widgets/test/test_boxlayoutdockwidget.py index 5df8df9..dd0ddf4 100644 --- a/src/silx/gui/widgets/test/test_boxlayoutdockwidget.py +++ b/src/silx/gui/widgets/test/test_boxlayoutdockwidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_elidedlabel.py b/src/silx/gui/widgets/test/test_elidedlabel.py index 693e43c..d7e2cdc 100644 --- a/src/silx/gui/widgets/test/test_elidedlabel.py +++ b/src/silx/gui/widgets/test/test_elidedlabel.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2020 European Synchrotron Radiation Facility +# Copyright (c) 2020-2022 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 @@ -27,8 +26,6 @@ __license__ = "MIT" __date__ = "08/06/2020" -import unittest - from silx.gui import qt from silx.gui.widgets.ElidedLabel import ElidedLabel from silx.gui.utils import testutils @@ -47,11 +44,18 @@ class TestElidedLabel(testutils.TestCaseQt): del self.label self.qapp.processEvents() + def testQLabelApi(self): + """Test overrided API from QLabel""" + self.label.setText("a") + assert self.label.text() == "a" + self.label.setToolTip("b") + assert self.label.toolTip() == "b" + def testElidedValue(self): """Test elided text""" raw = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" self.label.setText(raw) - self.label.setFixedWidth(30) + self.label.setFixedWidth(40) displayedText = qt.QLabel.text(self.label) self.assertNotEqual(raw, displayedText) self.assertIn("…", displayedText) @@ -98,3 +102,21 @@ class TestElidedLabel(testutils.TestCaseQt): displayedTooltip = qt.QLabel.toolTip(self.label) self.assertNotIn(raw1, displayedTooltip) self.assertIn(raw2, displayedTooltip) + + def testTooltip(self): + """Test tooltip when elided""" + self.label.setToolTip("Fooo") + assert self.label.toolTip() == "Fooo" + displayedTooltip = qt.QLabel.toolTip(self.label) + assert displayedTooltip == "Fooo" + + def testElidedTextAndTooltip(self): + """Test tooltip when elided""" + raw1 = "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn" + self.label.setText(raw1) + self.label.setFixedWidth(30) + self.label.setToolTip("Fooo") + displayedTooltip = qt.QLabel.toolTip(self.label) + assert self.label.toolTip() == "Fooo" + assert "Fooo" in displayedTooltip + assert raw1 in displayedTooltip diff --git a/src/silx/gui/widgets/test/test_flowlayout.py b/src/silx/gui/widgets/test/test_flowlayout.py index 85d7cfe..07f6697 100644 --- a/src/silx/gui/widgets/test/test_flowlayout.py +++ b/src/silx/gui/widgets/test/test_flowlayout.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_framebrowser.py b/src/silx/gui/widgets/test/test_framebrowser.py index 8233622..7fa621b 100644 --- a/src/silx/gui/widgets/test/test_framebrowser.py +++ b/src/silx/gui/widgets/test/test_framebrowser.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_hierarchicaltableview.py b/src/silx/gui/widgets/test/test_hierarchicaltableview.py index 302086a..8f6a2a0 100644 --- a/src/silx/gui/widgets/test/test_hierarchicaltableview.py +++ b/src/silx/gui/widgets/test/test_hierarchicaltableview.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_legendiconwidget.py b/src/silx/gui/widgets/test/test_legendiconwidget.py index fe320f6..cfebc62 100644 --- a/src/silx/gui/widgets/test/test_legendiconwidget.py +++ b/src/silx/gui/widgets/test/test_legendiconwidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2020 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_periodictable.py b/src/silx/gui/widgets/test/test_periodictable.py index de9e1af..f687e36 100644 --- a/src/silx/gui/widgets/test/test_periodictable.py +++ b/src/silx/gui/widgets/test/test_periodictable.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_printpreview.py b/src/silx/gui/widgets/test/test_printpreview.py index 8602666..b703d63 100644 --- a/src/silx/gui/widgets/test/test_printpreview.py +++ b/src/silx/gui/widgets/test/test_printpreview.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_rangeslider.py b/src/silx/gui/widgets/test/test_rangeslider.py index f829857..6ed50af 100644 --- a/src/silx/gui/widgets/test/test_rangeslider.py +++ b/src/silx/gui/widgets/test/test_rangeslider.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_tablewidget.py b/src/silx/gui/widgets/test/test_tablewidget.py index 09122ca..9b1e53f 100644 --- a/src/silx/gui/widgets/test/test_tablewidget.py +++ b/src/silx/gui/widgets/test/test_tablewidget.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/gui/widgets/test/test_threadpoolpushbutton.py b/src/silx/gui/widgets/test/test_threadpoolpushbutton.py index 3808be0..a3eca33 100644 --- a/src/silx/gui/widgets/test/test_threadpoolpushbutton.py +++ b/src/silx/gui/widgets/test/test_threadpoolpushbutton.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/image/__init__.py b/src/silx/image/__init__.py index 12bf320..72bd116 100644 --- a/src/silx/image/__init__.py +++ b/src/silx/image/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/image/_boundingbox.py b/src/silx/image/_boundingbox.py index 1c086b1..c016471 100644 --- a/src/silx/image/_boundingbox.py +++ b/src/silx/image/_boundingbox.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/image/backprojection.py b/src/silx/image/backprojection.py index 63f99ca..b208d3e 100644 --- a/src/silx/image/backprojection.py +++ b/src/silx/image/backprojection.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017 European Synchrotron Radiation Facility # diff --git a/src/silx/image/bilinear.pyx b/src/silx/image/bilinear.pyx index 14547f8..31ba354 100644 --- a/src/silx/image/bilinear.pyx +++ b/src/silx/image/bilinear.pyx @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- #cython: embedsignature=True, language_level=3 ## This is for optimisation #cython: boundscheck=False, wraparound=False, cdivision=True, initializedcheck=False, diff --git a/src/silx/image/marchingsquares/__init__.py b/src/silx/image/marchingsquares/__init__.py index a47a7f6..1c6f15e 100644 --- a/src/silx/image/marchingsquares/__init__.py +++ b/src/silx/image/marchingsquares/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018 European Synchrotron Radiation Facility # diff --git a/src/silx/image/marchingsquares/_mergeimpl.pyx b/src/silx/image/marchingsquares/_mergeimpl.pyx index 5a7a3b5..ce4786f 100644 --- a/src/silx/image/marchingsquares/_mergeimpl.pyx +++ b/src/silx/image/marchingsquares/_mergeimpl.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018-2020 European Synchrotron Radiation Facility # diff --git a/src/silx/image/marchingsquares/_skimage.py b/src/silx/image/marchingsquares/_skimage.py index d49eeb0..7fa97d5 100644 --- a/src/silx/image/marchingsquares/_skimage.py +++ b/src/silx/image/marchingsquares/_skimage.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018 European Synchrotron Radiation Facility # diff --git a/src/silx/image/marchingsquares/setup.py b/src/silx/image/marchingsquares/setup.py deleted file mode 100644 index 95998ab..0000000 --- a/src/silx/image/marchingsquares/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "23/04/2018" - -import os -import numpy -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('marchingsquares', parent_package, top_path) - config.add_subpackage('test') - - silx_include = os.path.join(top_path, "src", "silx", "utils", "include") - config.add_extension('_mergeimpl', - sources=['_mergeimpl.pyx'], - include_dirs=[numpy.get_include(), silx_include], - language='c++', - extra_link_args=['-fopenmp'], - extra_compile_args=['-fopenmp']) - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/image/marchingsquares/test/__init__.py b/src/silx/image/marchingsquares/test/__init__.py index 776bb73..0e66f61 100644 --- a/src/silx/image/marchingsquares/test/__init__.py +++ b/src/silx/image/marchingsquares/test/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx diff --git a/src/silx/image/marchingsquares/test/test_funcapi.py b/src/silx/image/marchingsquares/test/test_funcapi.py index d1be584..e9d2d7d 100644 --- a/src/silx/image/marchingsquares/test/test_funcapi.py +++ b/src/silx/image/marchingsquares/test/test_funcapi.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx diff --git a/src/silx/image/marchingsquares/test/test_mergeimpl.py b/src/silx/image/marchingsquares/test/test_mergeimpl.py index 07b94b5..bfa1263 100644 --- a/src/silx/image/marchingsquares/test/test_mergeimpl.py +++ b/src/silx/image/marchingsquares/test/test_mergeimpl.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx diff --git a/src/silx/image/medianfilter.py b/src/silx/image/medianfilter.py index 857f73d..1938357 100644 --- a/src/silx/image/medianfilter.py +++ b/src/silx/image/medianfilter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/image/phantomgenerator.py b/src/silx/image/phantomgenerator.py index 10b249b..1893368 100644 --- a/src/silx/image/phantomgenerator.py +++ b/src/silx/image/phantomgenerator.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/image/projection.py b/src/silx/image/projection.py index 5c76c35..0b58323 100644 --- a/src/silx/image/projection.py +++ b/src/silx/image/projection.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017 European Synchrotron Radiation Facility # diff --git a/src/silx/image/reconstruction.py b/src/silx/image/reconstruction.py index 875b66b..2ed95c0 100644 --- a/src/silx/image/reconstruction.py +++ b/src/silx/image/reconstruction.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017 European Synchrotron Radiation Facility # diff --git a/src/silx/image/setup.py b/src/silx/image/setup.py deleted file mode 100644 index 69d5b1b..0000000 --- a/src/silx/image/setup.py +++ /dev/null @@ -1,47 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-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. -# -# ############################################################################*/ - -__authors__ = ["J. Kieffer"] -__license__ = "MIT" -__date__ = "05/04/2018" - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('image', parent_package, top_path) - config.add_subpackage('test') - config.add_extension('bilinear', - sources=["bilinear.pyx"], - language='c') - config.add_extension('shapes', - sources=["shapes.pyx"], - language='c') - config.add_subpackage('marchingsquares') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/image/shapes.pyx b/src/silx/image/shapes.pyx index 9284811..26065e1 100644 --- a/src/silx/image/shapes.pyx +++ b/src/silx/image/shapes.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2017 European Synchrotron Radiation Facility diff --git a/src/silx/image/sift.py b/src/silx/image/sift.py index cb1e6bd..57599cd 100644 --- a/src/silx/image/sift.py +++ b/src/silx/image/sift.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017 European Synchrotron Radiation Facility # diff --git a/src/silx/image/test/__init__.py b/src/silx/image/test/__init__.py index 40b11a1..3ebbb32 100644 --- a/src/silx/image/test/__init__.py +++ b/src/silx/image/test/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx diff --git a/src/silx/image/test/test_bb.py b/src/silx/image/test/test_bb.py index 7427273..19f5f39 100644 --- a/src/silx/image/test/test_bb.py +++ b/src/silx/image/test/test_bb.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/image/test/test_bilinear.py b/src/silx/image/test/test_bilinear.py index 20ceb58..d0abe64 100644 --- a/src/silx/image/test/test_bilinear.py +++ b/src/silx/image/test/test_bilinear.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx (originally pyFAI) # https://github.com/silx-kit/silx diff --git a/src/silx/image/test/test_medianfilter.py b/src/silx/image/test/test_medianfilter.py index d3386a4..3215d69 100644 --- a/src/silx/image/test/test_medianfilter.py +++ b/src/silx/image/test/test_medianfilter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/image/test/test_shapes.py b/src/silx/image/test/test_shapes.py index 63abc00..1adc112 100644 --- a/src/silx/image/test/test_shapes.py +++ b/src/silx/image/test/test_shapes.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/image/test/test_tomography.py b/src/silx/image/test/test_tomography.py index f391a72..e697cbc 100644 --- a/src/silx/image/test/test_tomography.py +++ b/src/silx/image/test/test_tomography.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/image/tomography.py b/src/silx/image/tomography.py index 53855c1..d64afde 100644 --- a/src/silx/image/tomography.py +++ b/src/silx/image/tomography.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017 European Synchrotron Radiation Facility # diff --git a/src/silx/image/utils.py b/src/silx/image/utils.py index 996d010..2659112 100644 --- a/src/silx/image/utils.py +++ b/src/silx/image/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # /*########################################################################## # Copyright (C) 2019 European Synchrotron Radiation Facility # diff --git a/src/silx/io/__init__.py b/src/silx/io/__init__.py index b43d290..2ff6cad 100644 --- a/src/silx/io/__init__.py +++ b/src/silx/io/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/io/commonh5.py b/src/silx/io/commonh5.py index af4274f..25744b4 100644 --- a/src/silx/io/commonh5.py +++ b/src/silx/io/commonh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/convert.py b/src/silx/io/convert.py index ba9a254..a4c5dc3 100644 --- a/src/silx/io/convert.py +++ b/src/silx/io/convert.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/dictdump.py b/src/silx/io/dictdump.py index a24de42..d1bf8c4 100644 --- a/src/silx/io/dictdump.py +++ b/src/silx/io/dictdump.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2020 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -31,8 +30,11 @@ import json import logging import numpy import os.path -import sys import h5py +try: + import pint +except ImportError: + pint = None from .configdict import ConfigDict from .utils import is_group @@ -65,6 +67,8 @@ def _prepare_hdf5_write_value(array_like): ``numpy.array()`` (`str`, `list`, `numpy.ndarray`…) :return: ``numpy.ndarray`` ready to be written as an HDF5 dataset """ + if pint is not None and isinstance(array_like, pint.quantity.Quantity): + return numpy.array(array_like.magnitude) array = numpy.asarray(array_like) if numpy.issubdtype(array.dtype, numpy.bytes_): return numpy.array(array_like, dtype=vlen_bytes) @@ -294,6 +298,7 @@ def dicttoh5(treedict, h5file, h5path='/', del h5f[h5path] h5f.create_group(h5path) else: + logger.info(f'Cannot overwrite {h5f.file.filename}::{h5f[h5path].name} with update_mode="{update_mode}"') return else: h5f.create_group(h5path) @@ -330,6 +335,7 @@ def dicttoh5(treedict, h5file, h5path='/', else: # HDF5 dataset if exists and not change_allowed: + logger.info(f'Cannot modify dataset {h5f.file.filename}::{h5f[h5name].name} with update_mode="{update_mode}"') continue data = _prepare_hdf5_write_value(value) @@ -343,6 +349,7 @@ def dicttoh5(treedict, h5file, h5path='/', # Delete the existing dataset if update_mode != "replace": if not is_dataset(h5f[h5name]): + logger.info(f'Cannot overwrite {h5f.file.filename}::{h5f[h5name].name} with update_mode="{update_mode}"') continue attrs_backup = dict(h5f[h5name].attrs) del h5f[h5name] @@ -381,6 +388,7 @@ def dicttoh5(treedict, h5file, h5path='/', else: # Add/modify HDF5 attribute if exists and not change_allowed: + logger.info(f'Cannot modify attribute {h5f.file.filename}::{h5f[h5name].name}@{attr_name} with update_mode="{update_mode}"') continue data = _prepare_hdf5_write_value(value) h5a[attr_name] = data @@ -452,6 +460,7 @@ def nexus_to_h5_dict( value = h5py.SoftLink(first) elif is_link(value): key = key[1:] + if isinstance(value, Mapping): # HDF5 group key_has_nx_class = add_nx_class and _has_nx_class(treedict, key) @@ -460,9 +469,15 @@ def nexus_to_h5_dict( parents=parents+(key,), add_nx_class=add_nx_class, has_nx_class=key_has_nx_class) + + elif pint is not None and isinstance(value, pint.quantity.Quantity): + copy[key] = value.magnitude + copy[(key, "units")] = f"{value.units:~C}" + else: # HDF5 dataset or link copy[key] = value + if add_nx_class and not has_nx_class: _ensure_nx_class(copy, parents) return copy diff --git a/src/silx/io/fabioh5.py b/src/silx/io/fabioh5.py index af9b29a..c5ef964 100755 --- a/src/silx/io/fabioh5.py +++ b/src/silx/io/fabioh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/fioh5.py b/src/silx/io/fioh5.py index 75fe587..0a86bbf 100644 --- a/src/silx/io/fioh5.py +++ b/src/silx/io/fioh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2021 Timo Fuchs # diff --git a/src/silx/io/h5py_utils.py b/src/silx/io/h5py_utils.py index fb04152..139bff3 100644 --- a/src/silx/io/h5py_utils.py +++ b/src/silx/io/h5py_utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/nxdata/__init__.py b/src/silx/io/nxdata/__init__.py index 5bfa442..51efc68 100644 --- a/src/silx/io/nxdata/__init__.py +++ b/src/silx/io/nxdata/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/io/nxdata/_utils.py b/src/silx/io/nxdata/_utils.py index 12318f1..3aa3846 100644 --- a/src/silx/io/nxdata/_utils.py +++ b/src/silx/io/nxdata/_utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/io/nxdata/parse.py b/src/silx/io/nxdata/parse.py index d00f65b..0c9d7e7 100644 --- a/src/silx/io/nxdata/parse.py +++ b/src/silx/io/nxdata/parse.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2021 European Synchrotron Radiation Facility +# Copyright (c) 2017-2022 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 @@ -42,6 +41,8 @@ Other public functions: """ import json +from typing import Optional + import numpy from silx.io.utils import is_group, is_file, is_dataset, h5py_read_dataset @@ -684,8 +685,6 @@ class NXdata(object): return self.group[errors_name] # case of uncertainties dataset name provided in @uncertainties uncertainties_names = get_attr_as_unicode(self.group, "uncertainties") - if uncertainties_names is None: - uncertainties_names = get_attr_as_unicode(self.signal, "uncertainties") if isinstance(uncertainties_names, str): uncertainties_names = [uncertainties_names] if uncertainties_names is not None: @@ -720,15 +719,18 @@ class NXdata(object): if not self.is_valid: raise InvalidNXdataError("Unable to parse invalid NXdata") - # case of signal - signal_errors = self.signal_dataset_name + "_errors" - if "errors" in self.group and is_dataset(self.group["errors"]): - errors = "errors" - elif signal_errors in self.group and is_dataset(self.group[signal_errors]): - errors = signal_errors - else: - return None - return self.group[errors] + dataset_names = [ + # From NXData: + "errors", + # Not Nexus (VARIABLE_errors is only for axes), but supported anyway + self.signal_dataset_name + "_errors", + ] + for name in dataset_names: + entity = self.group.get(name) + if entity is not None and is_dataset(entity): + return entity + + return None @property def plot_style(self): @@ -970,35 +972,42 @@ def is_NXroot_with_default_NXdata(group, validate=True): validate=validate) -def get_default(group, validate=True): - """Return a :class:`NXdata` object corresponding to the default NXdata group - in the group specified as parameter. +def _get_default( + group, + validate: bool, + traversed: list, +) -> Optional[NXdata]: + if not is_group(group): + raise TypeError("Provided parameter is not a h5py-like group") - This function can find the NXdata if the group is already a NXdata, or - if it is a NXentry defining a default NXdata, or if it is a NXroot - defining such a default valid NXentry. + if get_attr_as_unicode(group, "NX_class") == "NXdata": + nxdata = NXdata(group, validate=validate) + return nxdata if nxdata.is_valid else None - Return None if no valid NXdata could be found. + default_name = get_attr_as_unicode(group, "default") + if default_name is None: + return None - :param group: h5py-like group following the Nexus specification - (NXdata, NXentry or NXroot). - :param bool validate: Set this to False if you are sure that group - is valid NXdata (i.e. :func:`silx.io.nxdata.is_valid_nxdata(group)` - returns True). Parameter provided for optimisation purposes. - :return: :class:`NXdata` object or None - :raise TypeError: if group is not a h5py-like group - """ - if not is_group(group): - raise TypeError("Provided parameter is not a h5py-like group") + default_entity = group.get(default_name) + if default_entity is None or default_entity in traversed: + return None - if is_NXroot_with_default_NXdata(group, validate=validate): - default_entry = group[group.attrs["default"]] - default_data = default_entry[default_entry.attrs["default"]] - elif is_group_with_default_NXdata(group, validate=validate): - default_data = group[group.attrs["default"]] - elif not validate or is_valid_nxdata(group): - default_data = group - else: + try: + return _get_default(default_entity, validate, traversed + [default_entity]) + except TypeError: return None - return NXdata(default_data, validate=False) + +def get_default(group, validate: bool=True) -> Optional[NXdata]: + """Find the default :class:`NXdata` group in given group. + + `@default` attributes are recursively followed until finding a group with + NX_class="NXdata". + Return None if no valid NXdata group could be found. + + :param group: h5py-like group to look for @default NXdata. + In cas it is a NXdata group, it is returned. + :param validate: False to disable checking the returned NXdata group. + :raise TypeError: if group is not a h5py-like group + """ + return _get_default(group, validate, []) diff --git a/src/silx/io/nxdata/write.py b/src/silx/io/nxdata/write.py index 9e84240..5dfe1df 100644 --- a/src/silx/io/nxdata/write.py +++ b/src/silx/io/nxdata/write.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017-2021 European Synchrotron Radiation Facility diff --git a/src/silx/io/octaveh5.py b/src/silx/io/octaveh5.py index 84fa726..67fb1e2 100644 --- a/src/silx/io/octaveh5.py +++ b/src/silx/io/octaveh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2020 European Synchrotron Radiation Facility # diff --git a/src/silx/io/rawh5.py b/src/silx/io/rawh5.py index ceabbdb..31b554d 100644 --- a/src/silx/io/rawh5.py +++ b/src/silx/io/rawh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # diff --git a/src/silx/io/setup.py b/src/silx/io/setup.py deleted file mode 100644 index 9cafa17..0000000 --- a/src/silx/io/setup.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: ascii -# -# JK: Numpy.distutils which imports this does not handle utf-8 in version<1.12 -# -# /*########################################################################## -# -# Copyright (c) 2016-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. -# -# ###########################################################################*/ - -__authors__ = ["P. Knobel", "V.A. Sole"] -__license__ = "MIT" -__date__ = "03/10/2016" - -import os -import sys - -from numpy.distutils.misc_util import Configuration - - -# Locale and platform management -SPECFILE_USE_GNU_SOURCE = os.getenv("SPECFILE_USE_GNU_SOURCE") -if SPECFILE_USE_GNU_SOURCE is None: - SPECFILE_USE_GNU_SOURCE = 0 - if sys.platform.lower().startswith("linux"): - warn = ("silx.io.specfile WARNING:", - "A cleaner locale independent implementation", - "may be achieved setting SPECFILE_USE_GNU_SOURCE to 1", - "For instance running this script as:", - "SPECFILE_USE_GNU_SOURCE=1 python setup.py build") - print(os.linesep.join(warn)) -else: - SPECFILE_USE_GNU_SOURCE = int(SPECFILE_USE_GNU_SOURCE) - -if sys.platform == "win32": - define_macros = [('WIN32', None), ('SPECFILE_POSIX', None)] -elif os.name.lower().startswith('posix'): - define_macros = [('SPECFILE_POSIX', None)] - # the best choice is to have _GNU_SOURCE defined - # as a compilation flag because that allows the - # use of strtod_l - if SPECFILE_USE_GNU_SOURCE: - define_macros = [('_GNU_SOURCE', 1)] -else: - define_macros = [] - - -def configuration(parent_package='', top_path=None): - config = Configuration('io', parent_package, top_path) - config.add_subpackage('test') - config.add_subpackage('nxdata') - - srcfiles = ['sfheader', 'sfinit', 'sflists', 'sfdata', 'sfindex', - 'sflabel', 'sfmca', 'sftools', 'locale_management'] - sources = [os.path.join('specfile', 'src', ffile + '.c') for ffile in srcfiles] - sources.append('specfile.pyx') - - config.add_extension('specfile', - sources=sources, - define_macros=define_macros, - include_dirs=[os.path.join('specfile', 'include')], - language='c') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/io/specfile.pyx b/src/silx/io/specfile.pyx index cb9e1a5..dde6d82 100644 --- a/src/silx/io/specfile.pyx +++ b/src/silx/io/specfile.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/io/specfile/include/SpecFileCython.h b/src/silx/io/specfile/include/SpecFileCython.h index 3225e13..77e9168 100644 --- a/src/silx/io/specfile/include/SpecFileCython.h +++ b/src/silx/io/specfile/include/SpecFileCython.h @@ -1,5 +1,4 @@ #/*########################################################################## -# coding: utf-8 # Copyright (C) 2016 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/silx/io/specfile/src/sflabel.c b/src/silx/io/specfile/src/sflabel.c index 61cbb3f..29e8f57 100644 --- a/src/silx/io/specfile/src/sflabel.c +++ b/src/silx/io/specfile/src/sflabel.c @@ -1,5 +1,5 @@ # /*########################################################################## -# Copyright (C) 1995-2019 European Synchrotron Radiation Facility +# Copyright (C) 1995-2022 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 @@ -87,6 +87,10 @@ DllExport double SfMotorPosByName( SpecFile *sf, long index, char *name, DllExport long SfAllMotorPos ( SpecFile *sf, long index, double **pos, int *error ); + +#define BUFFER_SIZE 256 +#define MIN(a, b) (((a) <= (b)) ? (a) : (b)) + /********************************************************************* * Function: char *SfLabel( sf, index, column, error ) @@ -180,7 +184,7 @@ SfLabel( SpecFile *sf, long index, long column, int *error ) DllExport long SfAllLabels( SpecFile *sf, long index, char ***labels, int *error ) { - static char tmplab[40]; + static char tmplab[BUFFER_SIZE]; char **labarr; char *onelabel; @@ -243,7 +247,7 @@ SfAllLabels( SpecFile *sf, long index, char ***labels, int *error ) for (i=0;ptr < buf + strlen(buf) -1;ptr++,i++) { if (*ptr==' ' && *(ptr+1) == ' ') { /* two spaces delimits one label */ - tmplab[i] = '\0'; + tmplab[MIN(i, BUFFER_SIZE-1)] = '\0'; labarr = (char **)realloc( labarr, (no_labels+1) * sizeof(char *)); onelabel = (char *) malloc (i+2); @@ -253,7 +257,7 @@ SfAllLabels( SpecFile *sf, long index, char ***labels, int *error ) no_labels++; i=-1; for(;*(ptr+1) == ' ' && ptr < buf+strlen(buf)-1;ptr++); - } else { + } else if (i < BUFFER_SIZE) { tmplab[i] = *ptr; } } @@ -318,7 +322,7 @@ SfAllMotors( SpecFile *sf, long index, char ***names, int *error ) char **motarr; char *onemot; - static char tmpmot[40]; + static char tmpmot[BUFFER_SIZE]; char *ptr; @@ -368,7 +372,7 @@ SfAllMotors( SpecFile *sf, long index, char ***names, int *error ) for(ptr=thisline;*ptr == ' ';ptr++); for (i=0;ptr < endline -2;ptr++,i++) { if (*ptr==' ' && *(ptr+1) == ' ') { - tmpmot[i] = '\0'; + tmpmot[MIN(i, BUFFER_SIZE-1)] = '\0'; motarr = (char **)realloc( motarr, (motct+1) * sizeof(char *)); onemot = (char *) malloc (i+2); @@ -378,7 +382,7 @@ SfAllMotors( SpecFile *sf, long index, char ***names, int *error ) motct++; i=-1; for(;*(ptr+1) == ' ' && ptr < endline -1;ptr++); - } else { + } else if (i < BUFFER_SIZE) { tmpmot[i] = *ptr; } } diff --git a/src/silx/io/specfile/src/sfmca.c b/src/silx/io/specfile/src/sfmca.c index ad13bae..246837e 100644 --- a/src/silx/io/specfile/src/sfmca.c +++ b/src/silx/io/specfile/src/sfmca.c @@ -298,6 +298,11 @@ SfGetMca( SpecFile *sf, long index, long number, double **retdata, int *error ) val = PyMcaAtof(strval); data[vals] = val; vals++; + } else if (i>0) { + strval[i] = '\0'; + val = PyMcaAtof(strval); + data[vals] = val; + vals++; } #ifndef _GNU_SOURCE #ifdef PYMCA_POSIX diff --git a/src/silx/io/specfile_wrapper.pxd b/src/silx/io/specfile_wrapper.pxd index 6770f7e..df629f7 100644 --- a/src/silx/io/specfile_wrapper.pxd +++ b/src/silx/io/specfile_wrapper.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/io/specfilewrapper.py b/src/silx/io/specfilewrapper.py index 01e185c..d8ee90b 100644 --- a/src/silx/io/specfilewrapper.py +++ b/src/silx/io/specfilewrapper.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*######################################################################### # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/io/spech5.py b/src/silx/io/spech5.py index df2021c..05ce9f0 100644 --- a/src/silx/io/spech5.py +++ b/src/silx/io/spech5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/spectoh5.py b/src/silx/io/spectoh5.py index fb3b739..0f4f1c5 100644 --- a/src/silx/io/spectoh5.py +++ b/src/silx/io/spectoh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/__init__.py b/src/silx/io/test/__init__.py index 244d090..3c723bb 100644 --- a/src/silx/io/test/__init__.py +++ b/src/silx/io/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_commonh5.py b/src/silx/io/test/test_commonh5.py index 27f6e8c..d554d27 100644 --- a/src/silx/io/test/test_commonh5.py +++ b/src/silx/io/test/test_commonh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_dictdump.py b/src/silx/io/test/test_dictdump.py index 4cafa9b..e31d7a8 100644 --- a/src/silx/io/test/test_dictdump.py +++ b/src/silx/io/test/test_dictdump.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -27,15 +26,21 @@ __authors__ = ["P. Knobel"] __license__ = "MIT" __date__ = "17/01/2018" -from collections import OrderedDict -import numpy + +from collections import defaultdict, OrderedDict +from copy import deepcopy +from io import BytesIO import os import tempfile import unittest -import h5py -from copy import deepcopy -from collections import defaultdict +import h5py +import numpy +try: + import pint +except ImportError: + pint = None +import pytest from silx.utils.testutils import LoggingValidator @@ -48,6 +53,13 @@ from ..utils import is_link from ..utils import h5py_read_dataset +@pytest.fixture +def tmp_h5py_file(): + with BytesIO() as buffer: + with h5py.File(buffer, mode="w") as h5file: + yield h5file + + def tree(): """Tree data structure as a recursive nested dictionary""" return defaultdict(tree) @@ -512,6 +524,22 @@ class TestDictToH5(H5DictTestCase): assert_append("replace") +@pytest.mark.skipif(pint is None, reason="Require pint") +def test_dicttoh5_pint(tmp_h5py_file): + ureg = pint.UnitRegistry() + treedict = { + "array_mm": pint.Quantity([1, 2, 3], ureg.mm), + "value_kg": 3 * ureg.kg, + } + + dicttoh5(treedict, tmp_h5py_file) + + result = h5todict(tmp_h5py_file) + assert set(treedict.keys()) == set(result.keys()) + for key, value in treedict.items(): + assert numpy.array_equal(result[key], value.magnitude) + + class TestH5ToDict(H5DictTestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() @@ -800,6 +828,22 @@ class TestDictToNx(H5DictTestCase): assert_append("replace", add_nx_class=True) +@pytest.mark.skipif(pint is None, reason="Require pint") +def test_dicttonx_pint(tmp_h5py_file): + ureg = pint.UnitRegistry() + treedict = { + "array_mm": pint.Quantity([1, 2, 3], ureg.mm), + "value_kg": 3 * ureg.kg, + } + + dictdump.dicttonx(treedict, tmp_h5py_file) + + result = dictdump.nxtodict(tmp_h5py_file) + for key, value in treedict.items(): + assert numpy.array_equal(result[key], value.magnitude) + assert result[f"{key}@units"] == f"{value.units:~C}" + + class TestNxToDict(H5DictTestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() diff --git a/src/silx/io/test/test_fabioh5.py b/src/silx/io/test/test_fabioh5.py index c410024..fdeb1c3 100755 --- a/src/silx/io/test/test_fabioh5.py +++ b/src/silx/io/test/test_fabioh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_fioh5.py b/src/silx/io/test/test_fioh5.py index 8ffb4ad..18200c9 100644 --- a/src/silx/io/test/test_fioh5.py +++ b/src/silx/io/test/test_fioh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2021 Timo Fuchs # diff --git a/src/silx/io/test/test_h5py_utils.py b/src/silx/io/test/test_h5py_utils.py index ea46eca..0d10a78 100644 --- a/src/silx/io/test/test_h5py_utils.py +++ b/src/silx/io/test/test_h5py_utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # @@ -449,3 +448,35 @@ class TestH5pyUtils(unittest.TestCase): f.write("0") with self.assertRaises(RetryTimeoutError): top_level_names_test(txtfilename, filename, **kw) + + @subtests + def test_retry_generator(self): + filename = self._new_filename() + ncausefailure = 3 + faildelay = 0.1 + sufficient_timeout = ncausefailure * (faildelay + 10) + insufficient_timeout = ncausefailure * faildelay * 0.5 + + @h5py_utils.retry() + def iter_data(filename, name, start_index=0): + nonlocal failcounter + if start_index <= 0: + with h5py_utils.File(filename) as h5file: + yield h5file[name][()] + if failcounter < ncausefailure: + time.sleep(faildelay) + failcounter += 1 + raise RetryError + if start_index <= 1: + with h5py_utils.File(filename) as h5file: + yield not h5file[name][()] + + failcounter = 0 + kw = {"retry_timeout": sufficient_timeout} + data = list(iter_data(filename, "/check", **kw)) + self.assertEqual(data, [True, False]) + + failcounter = 0 + kw = {"retry_timeout": insufficient_timeout} + with self.assertRaises(RetryTimeoutError): + list(iter_data(filename, "/check", **kw)) diff --git a/src/silx/io/test/test_nxdata.py b/src/silx/io/test/test_nxdata.py index 9025d6d..52a2b8a 100644 --- a/src/silx/io/test/test_nxdata.py +++ b/src/silx/io/test/test_nxdata.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -30,10 +29,13 @@ __date__ = "24/03/2020" import tempfile import unittest + import h5py import numpy +import pytest from .. import nxdata +from ..dictdump import dicttoh5 text_dtype = h5py.special_dtype(vlen=str) @@ -561,3 +563,144 @@ class TestSaveNXdata(unittest.TestCase): self.assertTrue(numpy.array_equal(nxd.axes[0], a0)) h5f.close() + + +class TestGetDefault: + """Test silx.io.nxdata.get_default function""" + + @pytest.fixture + def hdf5_file(self, tmp_path): + with h5py.File(tmp_path / "test_file.h5", "w") as h5f: + yield h5f + + def testDirectPath(self, hdf5_file): + dicttoh5( + { + ("", "default"): "/nxentry/nxprocess/nxdata", + "nxentry": { + "nxprocess": { + "nxdata": { + ("", "NX_class"): "NXdata", + ("", "signal"): "data", + "data": (1, 2, 3), + } + } + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert isinstance(default, nxdata.NXdata) + assert default.group.name == "/nxentry/nxprocess/nxdata" + + def testAbsolutePath(self, hdf5_file): + dicttoh5( + { + ("", "default"): "/nxentry", + "nxentry": { + ("", "default"): "/nxentry/nxprocess/nxdata", + "nxprocess": { + "nxdata": { + ("", "NX_class"): "NXdata", + ("", "signal"): "data", + "data": (1, 2, 3), + } + } + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert isinstance(default, nxdata.NXdata) + assert default.group.name == "/nxentry/nxprocess/nxdata" + + def testRelativePath(self, hdf5_file): + dicttoh5( + { + ("", "default"): "nxentry", + "nxentry": { + ("", "default"): "nxdata", + "nxdata": { + ("", "NX_class"): "NXdata", + ("", "signal"): "data", + "data": (1, 2, 3), + } + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert isinstance(default, nxdata.NXdata) + assert default.group.name == "/nxentry/nxdata" + + def testRelativePathSubdir(self, hdf5_file): + dicttoh5( + { + ("", "default"): "nxentry", + "nxentry": { + ("", "default"): "nxprocess/nxdata", + "nxprocess": { + "nxdata": { + ("", "NX_class"): "NXdata", + ("", "signal"): "data", + "data": (1, 2, 3), + } + } + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert isinstance(default, nxdata.NXdata) + assert default.group.name == "/nxentry/nxprocess/nxdata" + + def testRecursiveAbsolutePath(self, hdf5_file): + dicttoh5( + { + ("", "default"): "/nxentry", + "nxentry": { + ("", "default"): "/nxentry/nxprocess", + "nxprocess": { + ("", "default"): "/nxentry/nxprocess/nxdata", + "nxdata": { + ("", "NX_class"): "NXdata", + ("", "signal"): "data", + "data": (1, 2, 3), + } + } + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert isinstance(default, nxdata.NXdata) + assert default.group.name == "/nxentry/nxprocess/nxdata" + + def testRecursiveRelativePath(self, hdf5_file): + dicttoh5( + { + ("", "default"): "nxentry", + "nxentry": { + ("", "default"): "nxprocess", + "nxprocess": { + ("", "default"): "nxdata", + "nxdata": { + ("", "NX_class"): "NXdata", + ("", "signal"): "data", + "data": (1, 2, 3), + } + } + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert isinstance(default, nxdata.NXdata) + assert default.group.name == "/nxentry/nxprocess/nxdata" + + def testLoop(self, hdf5_file): + """Infinite loop of @default""" + dicttoh5( + { + ("", "default"): "/nxentry", + "nxentry": { + ("", "default"): "/nxentry", + } + }, + hdf5_file) + default = nxdata.get_default(hdf5_file) + assert default is None diff --git a/src/silx/io/test/test_octaveh5.py b/src/silx/io/test/test_octaveh5.py index 1c3b3e0..19b8ad6 100644 --- a/src/silx/io/test/test_octaveh5.py +++ b/src/silx/io/test/test_octaveh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_rawh5.py b/src/silx/io/test/test_rawh5.py index 236484d..947be0f 100644 --- a/src/silx/io/test/test_rawh5.py +++ b/src/silx/io/test/test_rawh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/io/test/test_specfile.py b/src/silx/io/test/test_specfile.py index 44cb08c..748e31c 100644 --- a/src/silx/io/test/test_specfile.py +++ b/src/silx/io/test/test_specfile.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_specfilewrapper.py b/src/silx/io/test/test_specfilewrapper.py index a1ba5f4..7d2ce60 100644 --- a/src/silx/io/test/test_specfilewrapper.py +++ b/src/silx/io/test/test_specfilewrapper.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_spech5.py b/src/silx/io/test/test_spech5.py index 1e67961..456a538 100644 --- a/src/silx/io/test/test_spech5.py +++ b/src/silx/io/test/test_spech5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_spectoh5.py b/src/silx/io/test/test_spectoh5.py index 66bf8d6..5465ece 100644 --- a/src/silx/io/test/test_spectoh5.py +++ b/src/silx/io/test/test_spectoh5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2019 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_url.py b/src/silx/io/test/test_url.py index 7346391..8cbfb34 100644 --- a/src/silx/io/test/test_url.py +++ b/src/silx/io/test/test_url.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # diff --git a/src/silx/io/test/test_utils.py b/src/silx/io/test/test_utils.py index cc34100..b9fc3ab 100644 --- a/src/silx/io/test/test_utils.py +++ b/src/silx/io/test/test_utils.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2019 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -30,7 +29,6 @@ import re import shutil import tempfile import unittest -import sys from .. import utils from ..._version import calc_hexversion @@ -40,6 +38,7 @@ import h5py from ..utils import h5ls from silx.io import commonh5 + import fabio __authors__ = ["P. Knobel"] @@ -921,3 +920,33 @@ def test_visitall_commonh5(): soft_link = visited_items["/group/soft_link"] assert isinstance(soft_link, commonh5.SoftLink) assert soft_link.path == "/group/dataset" + + +def test_match_hdf5(tmp_path): + """Test match function with HDF5 file""" + with h5py.File(tmp_path / "test_match.h5", "w") as h5f: + h5f.create_group("entry_0000/group") + h5f["entry_0000/data"] = 0 + h5f.create_group("entry_0001/group") + h5f["entry_0001/data"] = 1 + h5f.create_group("entry_0002") + h5f["entry_0003"] = 3 + + result = list(utils.match(h5f, "/entry_*/*")) + + assert sorted(result) == ['entry_0000/data', 'entry_0000/group', 'entry_0001/data', 'entry_0001/group'] + + +def test_match_commonh5(): + """Test match function with commonh5 objects""" + with commonh5.File("filename.file", mode="w") as fobj: + fobj.create_group("entry_0000/group") + fobj["entry_0000/data"] = 0 + fobj.create_group("entry_0001/group") + fobj["entry_0001/data"] = 1 + fobj.create_group("entry_0002") + fobj["entry_0003"] = 3 + + result = list(utils.match(fobj, "/entry_*/*")) + + assert sorted(result) == ['entry_0000/data', 'entry_0000/group', 'entry_0001/data', 'entry_0001/group'] diff --git a/src/silx/io/test/test_write_to_h5.py b/src/silx/io/test/test_write_to_h5.py index 06149c9..fe855e1 100644 --- a/src/silx/io/test/test_write_to_h5.py +++ b/src/silx/io/test/test_write_to_h5.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2021 European Synchrotron Radiation Facility # diff --git a/src/silx/io/url.py b/src/silx/io/url.py index a3c49e6..71b3103 100644 --- a/src/silx/io/url.py +++ b/src/silx/io/url.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/io/utils.py b/src/silx/io/utils.py index 642c6fb..0588138 100644 --- a/src/silx/io/utils.py +++ b/src/silx/io/utils.py @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -28,11 +27,13 @@ __license__ = "MIT" __date__ = "03/12/2020" import enum +import fnmatch import os.path import sys import time import logging import collections +from typing import Generator import urllib.parse import numpy @@ -538,6 +539,7 @@ class _MainNode(Proxy): def __init__(self, h5_node, h5_file): super(_MainNode, self).__init__(h5_node) + self.__node = h5_node self.__file = h5_file self.__class = get_h5_class(h5_node) @@ -823,6 +825,24 @@ def visitall(item): yield from _visitall(item, '') + +def match(group, path_pattern: str) -> Generator[str, None, None]: + """Generator of paths inside given h5py-like `group` matching `path_pattern`""" + if not is_group(group): + raise ValueError(f"Not a h5py-like group: {group}") + + path_parts = path_pattern.strip("/").split("/", 1) + for matching_path in fnmatch.filter(group.keys(), path_parts[0]): + if len(path_parts) == 1: # No more sub-path, stop recursion + yield matching_path + continue + + entity = group.get(matching_path) + if is_group(entity): + for matching_subpath in match(entity, path_parts[1]): + yield f"{matching_path}/{matching_subpath}" + + def get_data(url): """Returns a numpy data from an URL. diff --git a/src/silx/math/__init__.py b/src/silx/math/__init__.py index d8b7d81..e3e0447 100644 --- a/src/silx/math/__init__.py +++ b/src/silx/math/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/math/_colormap.pyx b/src/silx/math/_colormap.pyx index 70857f0..e1409fa 100644 --- a/src/silx/math/_colormap.pyx +++ b/src/silx/math/_colormap.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility diff --git a/src/silx/math/calibration.py b/src/silx/math/calibration.py index 658e2dc..79be585 100644 --- a/src/silx/math/calibration.py +++ b/src/silx/math/calibration.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018 European Synchrotron Radiation Facility # diff --git a/src/silx/math/chistogramnd.pyx b/src/silx/math/chistogramnd.pyx index 8484f35..3a0fa31 100644 --- a/src/silx/math/chistogramnd.pyx +++ b/src/silx/math/chistogramnd.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # @@ -675,7 +674,7 @@ def chistogramnd(sample, cdef int _histogramnd_double_double_double(double[:] sample, double[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -706,7 +705,7 @@ cdef int _histogramnd_double_double_double(double[:] sample, cdef int _histogramnd_double_float_double(double[:] sample, float[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -737,7 +736,7 @@ cdef int _histogramnd_double_float_double(double[:] sample, cdef int _histogramnd_double_int32_t_double(double[:] sample, cnumpy.int32_t[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -773,7 +772,7 @@ cdef int _histogramnd_double_int32_t_double(double[:] sample, cdef int _histogramnd_float_double_double(float[:] sample, double[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -804,7 +803,7 @@ cdef int _histogramnd_float_double_double(float[:] sample, cdef int _histogramnd_float_float_double(float[:] sample, float[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -835,7 +834,7 @@ cdef int _histogramnd_float_float_double(float[:] sample, cdef int _histogramnd_float_int32_t_double(float[:] sample, cnumpy.int32_t[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -871,7 +870,7 @@ cdef int _histogramnd_float_int32_t_double(float[:] sample, cdef int _histogramnd_int32_t_double_double(cnumpy.int32_t[:] sample, double[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -902,7 +901,7 @@ cdef int _histogramnd_int32_t_double_double(cnumpy.int32_t[:] sample, cdef int _histogramnd_int32_t_float_double(cnumpy.int32_t[:] sample, float[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -933,7 +932,7 @@ cdef int _histogramnd_int32_t_float_double(cnumpy.int32_t[:] sample, cdef int _histogramnd_int32_t_int32_t_double(cnumpy.int32_t[:] sample, cnumpy.int32_t[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -969,7 +968,7 @@ cdef int _histogramnd_int32_t_int32_t_double(cnumpy.int32_t[:] sample, cdef int _histogramnd_double_double_float(double[:] sample, double[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1000,7 +999,7 @@ cdef int _histogramnd_double_double_float(double[:] sample, cdef int _histogramnd_double_float_float(double[:] sample, float[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1031,7 +1030,7 @@ cdef int _histogramnd_double_float_float(double[:] sample, cdef int _histogramnd_double_int32_t_float(double[:] sample, cnumpy.int32_t[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1067,7 +1066,7 @@ cdef int _histogramnd_double_int32_t_float(double[:] sample, cdef int _histogramnd_float_double_float(float[:] sample, double[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1098,7 +1097,7 @@ cdef int _histogramnd_float_double_float(float[:] sample, cdef int _histogramnd_float_float_float(float[:] sample, float[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1129,7 +1128,7 @@ cdef int _histogramnd_float_float_float(float[:] sample, cdef int _histogramnd_float_int32_t_float(float[:] sample, cnumpy.int32_t[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1165,7 +1164,7 @@ cdef int _histogramnd_float_int32_t_float(float[:] sample, cdef int _histogramnd_int32_t_double_float(cnumpy.int32_t[:] sample, double[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1196,7 +1195,7 @@ cdef int _histogramnd_int32_t_double_float(cnumpy.int32_t[:] sample, cdef int _histogramnd_int32_t_float_float(cnumpy.int32_t[:] sample, float[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, @@ -1227,7 +1226,7 @@ cdef int _histogramnd_int32_t_float_float(cnumpy.int32_t[:] sample, cdef int _histogramnd_int32_t_int32_t_float(cnumpy.int32_t[:] sample, cnumpy.int32_t[:] weights, int n_dims, - int n_elem, + size_t n_elem, double[:] histo_range, int[:] n_bins, cnumpy.uint32_t[:] histo, diff --git a/src/silx/math/chistogramnd_lut.pyx b/src/silx/math/chistogramnd_lut.pyx index 3a3f05e..eed583a 100644 --- a/src/silx/math/chistogramnd_lut.pyx +++ b/src/silx/math/chistogramnd_lut.pyx @@ -1,6 +1,5 @@ -# coding: utf-8 # /*########################################################################## -# Copyright (C) 2016-2018 European Synchrotron Radiation Facility +# Copyright (C) 2016-2022 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 @@ -44,6 +43,7 @@ ctypedef fused cumul_t: cnumpy.int64_t ctypedef fused weights_t: + cnumpy.uint16_t cnumpy.float64_t cnumpy.float32_t cnumpy.int32_t @@ -108,7 +108,7 @@ def histogramnd_get_lut(sample, if histo_range.shape == (2,): pass elif histo_range.shape == (1, 2): - histo_range.reshape(-1) + histo_range = histo_range.reshape(-1) else: err_histo_range = True elif n_dims != 1 and histo_range.shape != (n_dims, 2): @@ -333,7 +333,7 @@ def _histogramnd_from_lut_fused(weights_t[:] i_weights, lut_t[:] i_lut, cnumpy.uint32_t[:] o_histo, cumul_t[:] o_weighted_histo, - int i_n_elems, + size_t i_n_elems, bint i_filt_min_weights, weights_t i_weight_min, bint i_filt_max_weights, @@ -360,7 +360,7 @@ def _histogramnd_from_lut_fused(weights_t[:] i_weights, @cython.cdivision(True) def _histogramnd_get_lut_fused(sample_t[:] i_sample, int i_n_dims, - int i_n_elems, + size_t i_n_elems, double[:] i_histo_range, int[:] i_n_bins, lut_t[:] o_lut, diff --git a/src/silx/math/colormap.py b/src/silx/math/colormap.py index 43b8949..8c05b63 100644 --- a/src/silx/math/colormap.py +++ b/src/silx/math/colormap.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility diff --git a/src/silx/math/combo.pyx b/src/silx/math/combo.pyx index e24edda..07197c6 100644 --- a/src/silx/math/combo.pyx +++ b/src/silx/math/combo.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/math/fft/__init__.py b/src/silx/math/fft/__init__.py index ea12cd6..6966a60 100644 --- a/src/silx/math/fft/__init__.py +++ b/src/silx/math/fft/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 __authors__ = ["P. Paleo"] __license__ = "MIT" diff --git a/src/silx/math/fft/basefft.py b/src/silx/math/fft/basefft.py index 854ca37..c608fde 100644 --- a/src/silx/math/fft/basefft.py +++ b/src/silx/math/fft/basefft.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/math/fft/clfft.py b/src/silx/math/fft/clfft.py index dad8ec1..2e41e47 100644 --- a/src/silx/math/fft/clfft.py +++ b/src/silx/math/fft/clfft.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility @@ -74,7 +73,7 @@ class CLFFT(BaseFFT): if not(__have_clfft__) or not(__have_clfft__): raise ImportError("Please install pyopencl and gpyfft >= %s to use the OpenCL back-end" % __required_gpyfft_version__) - super(CLFFT, self).__init__( + super().__init__( shape=shape, dtype=dtype, template=template, diff --git a/src/silx/math/fft/cufft.py b/src/silx/math/fft/cufft.py index 848f3e6..4bc7806 100644 --- a/src/silx/math/fft/cufft.py +++ b/src/silx/math/fft/cufft.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility @@ -61,7 +60,7 @@ class CUFFT(BaseFFT): if not(__have_cufft__) or not(__have_cufft__): raise ImportError("Please install pycuda and scikit-cuda to use the CUDA back-end") - super(CUFFT, self).__init__( + super().__init__( shape=shape, dtype=dtype, template=template, diff --git a/src/silx/math/fft/fft.py b/src/silx/math/fft/fft.py index eb0d73b..23de0cb 100644 --- a/src/silx/math/fft/fft.py +++ b/src/silx/math/fft/fft.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility @@ -24,9 +23,7 @@ # # ###########################################################################*/ from .fftw import FFTW -from .clfft import CLFFT from .npfft import NPFFT -from .cufft import CUFFT def FFT( @@ -64,27 +61,33 @@ def FFT( :param str normalize: Whether to normalize FFT and IFFT. Possible values are: * "rescale": in this case, Fourier data is divided by "N" - before IFFT, so that (FFT(data)) = data + before IFFT, so that IFFT(FFT(data)) = data. + This corresponds to numpy norm=None i.e norm="backward". * "ortho": in this case, FFT and IFFT are adjoint of eachother, the transform is unitary. Both FFT and IFFT are scaled with 1/sqrt(N). * "none": no normalizatio is done : IFFT(FFT(data)) = data*N :param str backend: FFT Backend to use. Value can be "numpy", "fftw", "opencl", "cuda". """ - backends = { - "numpy": NPFFT, - "np": NPFFT, - "fftw": FFTW, - "opencl": CLFFT, - "clfft": CLFFT, - "cuda": CUFFT, - "cufft": CUFFT, - } - + backends = ["numpy", "fftw", "opencl", "cuda"] backend = backend.lower() - if backend not in backends: + if backend in ["numpy", "np"]: + fft_cls = NPFFT + elif backend == "fftw": + fft_cls = FFTW + elif backend in ["opencl", "clfft"]: + # Late import for creating context only if needed + from .clfft import CLFFT + + fft_cls = CLFFT + elif backend in ["cuda", "cufft"]: + # Late import for creating context only if needed + from .cufft import CUFFT + + fft_cls = CUFFT + else: raise ValueError("Unknown backend %s, available are %s" % (backend, backends)) - F = backends[backend]( + F = fft_cls( shape=shape, dtype=dtype, template=template, diff --git a/src/silx/math/fft/fftw.py b/src/silx/math/fft/fftw.py index ff6966c..797543b 100644 --- a/src/silx/math/fft/fftw.py +++ b/src/silx/math/fft/fftw.py @@ -1,8 +1,7 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2018-2019 European Synchrotron Radiation Facility +# Copyright (c) 2018-2022 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 @@ -23,11 +22,18 @@ # THE SOFTWARE. # # ###########################################################################*/ -import numpy as np +import os +from sys import executable as sys_executable +from socket import gethostname +from tempfile import gettempdir +from pathlib import Path +import numpy as np from .basefft import BaseFFT, check_version + try: import pyfftw + __have_fftw__ = True except ImportError: __have_fftw__ = False @@ -53,6 +59,7 @@ class FFTW(BaseFFT): :param int num_threads: Number of threads for computing FFT. """ + def __init__( self, shape=None, @@ -64,9 +71,12 @@ class FFTW(BaseFFT): check_alignment=False, num_threads=1, ): - if not(__have_fftw__): - raise ImportError("Please install pyfftw >= %s to use the FFTW back-end" % __required_pyfftw_version__) - super(FFTW, self).__init__( + if not (__have_fftw__): + raise ImportError( + "Please install pyfftw >= %s to use the FFTW back-end" + % __required_pyfftw_version__ + ) + super().__init__( shape=shape, dtype=dtype, template=template, @@ -87,17 +97,55 @@ class FFTW(BaseFFT): "data_out": self.data_out, } + # About normalization with norm="none", issues about pyfftw version : + # --------------- pyfftw 0.12 --------------- + # FFT : + # normalise_idft --> 1 + # not normalise_idft --> 1 + # IFFT : + # normalise_idft --> 1 / N + # not normalise_idft --> 1 + # --------------- pyfftw 0.13 --------------- + # FFT : + # normalise_idft --> 1 + # not normalise_idft --> 1 / N (this normalization is incorrect, doc says contrary) + # IFFT : + # normalise_idft --> 1 / N + # not normalise_idft --> 1 + + # Solution : + # select 'normalise_idft' for FFT and 'not normalise_idft' for IFFT + # => behavior is the same in both version :) + def set_fftw_flags(self): - self.fftw_flags = ('FFTW_MEASURE', ) # TODO - self.fftw_planning_timelimit = None # TODO + self.fftw_flags = ("FFTW_MEASURE",) # TODO + self.fftw_planning_timelimit = None # TODO + + # To skip normalization on norm="none", we should + # flip 'normalise_idft' to normalize no-where (see comments up): + # + # and : + # ortho (orthogonal normalization) + # ortho = True : forward -> 1/sqrt(N), backward -> 1/sqrt(N) + self.fftw_norm_modes = { - "rescale": {"ortho": False, "normalize": True}, - "ortho": {"ortho": True, "normalize": False}, - "none": {"ortho": False, "normalize": False}, + "rescale": ( + {"ortho": False, "normalise_idft": True}, # fft + {"ortho": False, "normalise_idft": True}, # ifft + ), + "ortho": ( + {"ortho": True, "normalise_idft": False}, # fft + {"ortho": True, "normalise_idft": False}, # ifft + ), + "none": ( + {"ortho": False, "normalise_idft": True}, # fft + {"ortho": False, "normalise_idft": False}, # ifft + ), } if self.normalize not in self.fftw_norm_modes: - raise ValueError("Unknown normalization mode %s. Possible values are %s" % - (self.normalize, self.fftw_norm_modes.keys()) + raise ValueError( + "Unknown normalization mode %s. Possible values are %s" + % (self.normalize, self.fftw_norm_modes.keys()) ) self.fftw_norm_mode = self.fftw_norm_modes[self.normalize] @@ -106,12 +154,12 @@ class FFTW(BaseFFT): def check_array(self, array, shape, dtype, copy=True): if array.shape != shape: - raise ValueError("Invalid data shape: expected %s, got %s" % - (shape, array.shape) + raise ValueError( + "Invalid data shape: expected %s, got %s" % (shape, array.shape) ) if array.dtype != dtype: - raise ValueError("Invalid data type: expected %s, got %s" % - (dtype, array.dtype) + raise ValueError( + "Invalid data type: expected %s, got %s" % (dtype, array.dtype) ) def set_data(self, self_array, array, shape, dtype, copy=True, name=None): @@ -133,7 +181,7 @@ class FFTW(BaseFFT): if id(self.refs[name]) == id(array): # nothing to do: fft is performed on self.data_in or self.data_out arr_to_use = self.refs[name] - if self.check_alignment and not(pyfftw.is_byte_aligned(array)): + if self.check_alignment and not (pyfftw.is_byte_aligned(array)): # If the array is not properly aligned, # create a temp. array copy it to self.data_in or self.data_out self_array[:] = array[:] @@ -151,13 +199,10 @@ class FFTW(BaseFFT): self.data_in, self.data_out, axes=self.axes, - direction='FFTW_FORWARD', + direction="FFTW_FORWARD", flags=self.fftw_flags, threads=self.num_threads, planning_timelimit=self.fftw_planning_timelimit, - # the following seems to be taken into account only when using __call__ - ortho=self.fftw_norm_mode["ortho"], - normalise_idft=self.fftw_norm_mode["normalize"], ) def compute_inverse_plan(self): @@ -165,13 +210,10 @@ class FFTW(BaseFFT): self.data_out, self.data_in, axes=self.axes, - direction='FFTW_BACKWARD', + direction="FFTW_BACKWARD", flags=self.fftw_flags, threads=self.num_threads, planning_timelimit=self.fftw_planning_timelimit, - # the following seem to be taken into account only when using __call__ - ortho=self.fftw_norm_mode["ortho"], - normalise_idft=self.fftw_norm_mode["normalize"], ) def fft(self, array, output=None): @@ -187,8 +229,9 @@ class FFTW(BaseFFT): data_out = self.set_output_data(output, copy=False) self.plan_forward.update_arrays(data_in, data_out) # execute.__call__ does both update_arrays() and normalization - self.plan_forward( - ortho=self.fftw_norm_mode["ortho"], + self.plan_forward( # [0] --> fft + ortho=self.fftw_norm_mode[0]["ortho"], + normalise_idft=self.fftw_norm_mode[0]["normalise_idft"], ) self.plan_forward.update_arrays(self.refs["data_in"], self.refs["data_out"]) return data_out @@ -204,11 +247,119 @@ class FFTW(BaseFFT): """ data_in = self.set_output_data(array, copy=False) data_out = self.set_input_data(output, copy=False) - self.plan_inverse.update_arrays(data_in, data_out) + self.plan_inverse.update_arrays( + data_in, data_out + ) # TODO why in/out when it is out/in everywhere else in the function # execute.__call__ does both update_arrays() and normalization - self.plan_inverse( - ortho=self.fftw_norm_mode["ortho"], - normalise_idft=self.fftw_norm_mode["normalize"] + self.plan_inverse( # [1] --> ifft + ortho=self.fftw_norm_mode[1]["ortho"], + normalise_idft=self.fftw_norm_mode[1]["normalise_idft"], ) self.plan_inverse.update_arrays(self.refs["data_out"], self.refs["data_in"]) return data_out + + + +def get_wisdom_metadata(): + """ + Get metadata on the current platform. + FFTW wisdom works with varying performance depending on whether the plans are re-used + on the same machine/architecture/etc. + For more information: https://www.fftw.org/fftw3_doc/Caveats-in-Using-Wisdom.html + """ + return { + # "venv" + "executable": sys_executable, + # encapsulates sys.platform, platform.machine(), platform.architecture(), platform.libc_ver(), ... + "hostname": gethostname(), + "available_threads": len(os.sched_getaffinity(0)), + } + + +def export_wisdom(fname, on_existing="overwrite"): + """ + Export the current FFTW wisdom to a file. + + :param str fname: + Path to the file where the wisdom is to be exported + :param str on_existing: + What do do when the target file already exists. + Possible options are: + - raise: raise an error and exit + - overwrite: overwrite the file with the current wisdom + - append: Import the already existing wisdom, and dump the newly combined wisdom to this file + """ + if os.path.isfile(fname): + if on_existing == "raise": + raise ValueError("File already exists: %s" % fname) + if on_existing == "append": + import_wisdom(fname, on_mismatch="ignore") # ? + current_wisdom = pyfftw.export_wisdom() + res = get_wisdom_metadata() + for i, w in enumerate(current_wisdom): + res[str(i)] = np.array(w) + np.savez_compressed(fname, **res) + + +def import_wisdom(fname, match=["hostname"], on_mismatch="warn"): + """ + Import FFTW wisdom for a .npz file. + + :param str fname: + Path to the .npz file containing FFTW wisdom + :param list match: + List of elements that must match when importing wisdom. + If match=["hostname"] (default), this class will only load wisdom that was saved + on the current machine, and discard everything else. + If match=["hostname", "executable"], wisdom will only be loaded if the file was + created on the same machine and by the same python executable. + :param str on_mismatch: + What to do when the file wisdom does not match the current platform. + Available options: + - "raise": raise an error (crash) + - "warn": print a warning, don't crash + - "ignore": do nothing + """ + def handle_mismatch(item, loaded_value, current_value): + msg = "Platform configuration mismatch: %s: currently have '%s', loaded '%s'" % (item, current_value, loaded_value) + if on_mismatch == "raise": + raise ValueError(msg) + if on_mismatch == "warn": + print(msg) + + wis_metadata = get_wisdom_metadata() + loaded_wisdom = np.load(fname) + for metadata_name in match: + if metadata_name not in wis_metadata: + raise ValueError( + "Cannot match metadata '%s'. Available are: %s" % (match, str(wis_metadata.keys())) + ) + if loaded_wisdom[metadata_name] != wis_metadata[metadata_name]: + handle_mismatch(metadata_name, loaded_wisdom[metadata_name], wis_metadata[metadata_name]) + return + w = tuple(loaded_wisdom[k][()] for k in loaded_wisdom.keys() if k not in wis_metadata.keys()) + pyfftw.import_wisdom(w) + + +def get_wisdom_file(directory=None, name_template="fftw_wisdom_{whoami}_{hostname}.npz", create_dirs=True): + """ + Get a file path for storing FFTW wisdom. + + :param str directory: + Directory where the file is created. By default, files are written in a temporary directory. + :param str name_template: + File name pattern. The following patterns can be used: + - {whoami}: current username + - {hostname}: machine name + :param bool create_dirs: + Whether to create (possibly nested) directories if needed. + """ + directory = directory or gettempdir() + file_basename = name_template.format( + whoami=os.getlogin(), + hostname=gethostname() + ) + out_file = os.path.join(directory, file_basename) + if create_dirs: + Path(os.path.dirname(out_file)).mkdir(parents=True, exist_ok=True) + return out_file diff --git a/src/silx/math/fft/npfft.py b/src/silx/math/fft/npfft.py index 20351de..fc7d1c9 100644 --- a/src/silx/math/fft/npfft.py +++ b/src/silx/math/fft/npfft.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility @@ -24,6 +23,8 @@ # # ###########################################################################*/ import numpy as np +import warnings +from pkg_resources import parse_version from .basefft import BaseFFT @@ -33,6 +34,7 @@ class NPFFT(BaseFFT): Please see FFT class for parameters help. """ + def __init__( self, shape=None, @@ -42,7 +44,7 @@ class NPFFT(BaseFFT): axes=None, normalize="rescale", ): - super(NPFFT, self).__init__( + super().__init__( shape=shape, dtype=dtype, template=template, @@ -55,13 +57,34 @@ class NPFFT(BaseFFT): if template is not None and np.isrealobj(template): self.real_transform = True # For numpy functions. - # TODO Issue warning if user wants ifft(fft(data)) = N*data ? - if normalize != "ortho": - self.normalize = None + + self.set_fft_norm() self.set_fft_functions() - #~ self.allocate_arrays() # not needed for this backend self.compute_plans() + def set_fft_norm(self): + # backward, forward indicates the direction in which the + # normalisation is done. default is "backward" + + # rescale is default norm with numpy, no need of keywords + # if normalize == "rescale": # normalisation 1/N on ifft + self.numpy_args_fft = {} + self.numpy_args_ifft = {} + + if self.normalize == "ortho": # normalization 1/sqrt(N) on both fft & ifft + self.numpy_args_fft = {"norm": "ortho"} + self.numpy_args_ifft = {"norm": "ortho"} + + elif self.normalize == "none": # no normalisation on both fft & ifft + if parse_version(np.version.version) < parse_version("1.20"): + # "backward" & "forward" keywords were introduced in 1.20 and we support numpy >= 1.8 + warnings.warn( + "Numpy version %s does not allow to non-normalization. Effective normalization will be 'rescale'" + % (np.version.version) + ) # default 'rescale' normalization + else: + self.numpy_args_fft = {"norm": "backward"} + self.numpy_args_ifft = {"norm": "forward"} def set_fft_functions(self): # (fwd, inv) = _fft_functions[is_real][ndim] @@ -75,34 +98,33 @@ class NPFFT(BaseFFT): 1: (np.fft.fft, np.fft.ifft), 2: (np.fft.fft2, np.fft.ifft2), 3: (np.fft.fftn, np.fft.ifftn), - } + }, } - def _allocate(self, shape, dtype): - return np.zeros(self.queue, shape, dtype=dtype) - + return np.zeros(shape, dtype=dtype) def compute_plans(self): ndim = len(self.shape) funcs = self._fft_functions[self.real_transform][np.minimum(ndim, 3)] - if np.version.version[:4] in ["1.8.", "1.9."]: - # norm keyword was introduced in 1.10 and we support numpy >= 1.8 - self.numpy_args = {} - else: - self.numpy_args = {"norm": self.normalize} + + # Set norm + # self.numpy_args_fft & self.numpy_args_ifft already set in set_fft_norm + # Batched transform if (self.user_axes is not None) and len(self.user_axes) < ndim: - funcs = self._fft_functions[self.real_transform][np.minimum(ndim-1, 3)] - self.numpy_args["axes"] = self.user_axes + funcs = self._fft_functions[self.real_transform][np.minimum(ndim - 1, 3)] + self.numpy_args_fft["axes"] = self.user_axes + self.numpy_args_ifft["axes"] = self.user_axes # Special case of batched 1D transform on 2D data if ndim == 2: assert len(self.user_axes) == 1 - self.numpy_args["axis"] = self.user_axes[0] - self.numpy_args.pop("axes") + self.numpy_args_fft["axis"] = self.user_axes[0] + self.numpy_args_fft.pop("axes") + self.numpy_args_ifft["axis"] = self.user_axes[0] + self.numpy_args_ifft.pop("axes") self.numpy_funcs = funcs - def fft(self, array): """ Perform a (forward) Fast Fourier Transform. @@ -110,8 +132,7 @@ class NPFFT(BaseFFT): :param numpy.ndarray array: Input data. Must be consistent with the current context. """ - return self.numpy_funcs[0](array, **self.numpy_args) - + return self.numpy_funcs[0](array, **self.numpy_args_fft) def ifft(self, array): """ @@ -120,5 +141,4 @@ class NPFFT(BaseFFT): :param numpy.ndarray array: Input data. Must be consistent with the current context. """ - return self.numpy_funcs[1](array, **self.numpy_args) - + return self.numpy_funcs[1](array, **self.numpy_args_ifft) diff --git a/src/silx/math/fft/setup.py b/src/silx/math/fft/setup.py deleted file mode 100644 index 76bb864..0000000 --- a/src/silx/math/fft/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["P. Naudet"] -__license__ = "MIT" -__date__ = "12/12/2018" - -import numpy -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('fft', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/math/fft/test/__init__.py b/src/silx/math/fft/test/__init__.py index ad9836c..d076ee3 100644 --- a/src/silx/math/fft/test/__init__.py +++ b/src/silx/math/fft/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2019 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fft/test/test_fft.py b/src/silx/math/fft/test/test_fft.py index 19becb8..b696317 100644 --- a/src/silx/math/fft/test/test_fft.py +++ b/src/silx/math/fft/test/test_fft.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -25,10 +24,13 @@ # ###########################################################################*/ """Test of the FFT module""" +from os import path +import logging import numpy as np import unittest -import logging +from pkg_resources import parse_version import pytest +from tempfile import TemporaryDirectory try: from scipy.misc import ascent __have_scipy = True @@ -38,11 +40,43 @@ from silx.utils.testutils import ParametricTestCase from silx.math.fft.fft import FFT from silx.math.fft.clfft import __have_clfft__ from silx.math.fft.cufft import __have_cufft__ -from silx.math.fft.fftw import __have_fftw__ +from silx.math.fft.fftw import __have_fftw__, import_wisdom, export_wisdom, get_wisdom_file +if __have_cufft__: + import atexit + import pycuda.driver as cuda + from pycuda.tools import clear_context_caches -logger = logging.getLogger(__name__) +def get_cuda_context(device_id=None, cleanup_at_exit=True): + """ + Create or get a CUDA context. + """ + current_ctx = cuda.Context.get_current() + # If a context already exists, use this one + # TODO what if the device used is different from device_id ? + if current_ctx is not None: + return current_ctx + # Otherwise create a new context + cuda.init() + + if device_id is None: + device_id = 0 + # Use the Context obtained by retaining the device's primary context, + # which is the one used by the CUDA runtime API (ex. scikit-cuda). + # Unlike Context.make_context(), the newly-created context is not made current. + context = cuda.Device(device_id).retain_primary_context() + context.push() + # Register a clean-up function at exit + def _finish_up(context): + if context is not None: + context.pop() + context = None + clear_context_caches() + if cleanup_at_exit: + atexit.register(_finish_up, context) + return context +logger = logging.getLogger(__name__) class TransformInfos(object): def __init__(self): @@ -113,7 +147,7 @@ class TestFFT(ParametricTestCase): @unittest.skipIf(not __have_cufft__, "cuda back-end requires pycuda and scikit-cuda") def test_cuda(self): - import pycuda.autoinit + get_cuda_context() # Error is higher when using cuda. fast_math mode ? self.tol[np.dtype("float32")] *= 2 @@ -196,6 +230,90 @@ class TestFFT(ParametricTestCase): ) + # Test normalizations. silx FFT has three normalization modes: + # - "rescale" (default). FFT is unscaled, IFFT is scaled by 1/N. + # This corresponds to numpy normalize=None i.e normalize="backward" + # - "ortho": FFT/IFFT are both scaled with 1/sqrt(N) so that FFT is unitary. + # - "none": Neither FFT nor IFFT are not scaled, so IFFT(FFT(array)) = N*array + + norms_backends_support = { + "numpy": { + "supported_normalizations": ["rescale", "ortho", "none"], + }, + "fftw": { + "supported_normalizations": ["rescale", "ortho", "none"], + }, + "opencl": { + "supported_normalizations": ["rescale"], + }, + "cuda": { + "supported_normalizations": ["rescale", "none"], + } + } + + @staticmethod + def _compute_numpy_normalized_fft(data, axes, silx_normalization_mode): + if silx_normalization_mode in ["rescale", "none"]: + return np.fft.rfftn(data, axes=axes, norm=None) + elif silx_normalization_mode == "ortho": + return np.fft.rfftn(data, axes=axes, norm="ortho") + else: + raise ValueError("Unknown normalization mode %s" % silx_normalization_mode) + + @staticmethod + def _compute_numpy_normalized_ifft(data, axes, silx_normalization_mode): + if silx_normalization_mode == "rescale": + return np.fft.irfftn(data, axes=axes, norm=None) + elif silx_normalization_mode == "ortho": + return np.fft.irfftn(data, axes=axes, norm="ortho") + elif silx_normalization_mode == "none": + res = np.fft.irfftn(data, axes=axes, norm=None) + # This assumes a FFT on all the axes, won't work on batched FFT + N = res.size + return res * N + else: + raise ValueError("Unknown normalization mode %s" % silx_normalization_mode) + + @unittest.skipIf(not __have_fftw__, "fftw back-end requires pyfftw") + def test_norms_fftw(self): + return self._test_norms_with_backend("fftw") + + @unittest.skipIf( + parse_version(np.version.version) <= parse_version("1.19.5"), + "normalization does not work for numpy <= 1.19.5" + ) + def test_norms_numpy(self): + return self._test_norms_with_backend("numpy") + + @unittest.skipIf(not __have_clfft__, "opencl back-end requires pyopencl and gpyfft") + def test_norms_opencl(self): + from silx.opencl.common import ocl + if ocl is not None: + return self._test_norms_with_backend("opencl") + + @unittest.skipIf(not __have_cufft__, "cuda back-end requires pycuda and scikit-cuda") + def test_norms_cuda(self): + get_cuda_context() + return self._test_norms_with_backend("cuda") + + def _test_norms_with_backend(self, backend_name): + backend_params = self.norms_backends_support[backend_name] + + data = self.test_data.data + tol = self.tol[np.dtype(data.dtype)] + + for norm in backend_params["supported_normalizations"]: + fft = FFT(template=data, backend=backend_name, normalize=norm) + res = fft.fft(data) + ref = self._compute_numpy_normalized_fft(data, fft.axes, norm) + assert np.allclose(res, ref, atol=tol, rtol=tol), "Something wrong with %s norm=%s" % (backend_name, norm) + + res2 = fft.ifft(res) + ref2 = self._compute_numpy_normalized_ifft(ref, fft.axes, norm) + # unscaled IFFT yields very large values. Use a relatively high "atol" + assert np.allclose(res2, ref2, atol=res2.max()/1e6), "Something wrong with I%s norm=%s" % (backend_name, norm) + + @unittest.skipUnless(__have_scipy, "scipy is missing") class TestNumpyFFT(ParametricTestCase): """ @@ -255,3 +373,23 @@ class TestNumpyFFT(ParametricTestCase): res2 = F.ifft(res) ref2 = np_ifft(ref) self.assertTrue(np.allclose(res2, ref2)) + + +@pytest.mark.skipif(not(__have_fftw__), reason="Need fftw/pyfftw for this test") +def test_fftw_wisdom(): + """ + Test FFTW wisdom import/export mechanism + """ + + assert path.isdir(path.dirname(get_wisdom_file())) # Default: tempdir.gettempdir() + + with TemporaryDirectory(prefix="test_fftw_wisdom") as dname: + subdir = path.join(dname, "subdir") + get_wisdom_file(directory=subdir, create_dirs=False) + assert not(path.isdir(subdir)) + fname = get_wisdom_file(directory=subdir, create_dirs=True) + assert path.isdir(subdir) + export_wisdom(fname) + assert path.isfile(fname) + import_wisdom(fname) + diff --git a/src/silx/math/fit/__init__.py b/src/silx/math/fit/__init__.py index 29e6a9e..7dd6d32 100644 --- a/src/silx/math/fit/__init__.py +++ b/src/silx/math/fit/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/bgtheories.py b/src/silx/math/fit/bgtheories.py index 631c43e..d0f4987 100644 --- a/src/silx/math/fit/bgtheories.py +++ b/src/silx/math/fit/bgtheories.py @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # # Copyright (c) 2004-2020 European Synchrotron Radiation Facility diff --git a/src/silx/math/fit/filters.pyx b/src/silx/math/fit/filters.pyx index da1f6f5..054d129 100644 --- a/src/silx/math/fit/filters.pyx +++ b/src/silx/math/fit/filters.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/filters_wrapper.pxd b/src/silx/math/fit/filters_wrapper.pxd index e4f7c72..e09de32 100644 --- a/src/silx/math/fit/filters_wrapper.pxd +++ b/src/silx/math/fit/filters_wrapper.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/fitmanager.py b/src/silx/math/fit/fitmanager.py index 226e047..cbb1e34 100644 --- a/src/silx/math/fit/fitmanager.py +++ b/src/silx/math/fit/fitmanager.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*######################################################################### # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/math/fit/fittheories.py b/src/silx/math/fit/fittheories.py index 5461416..76f2478 100644 --- a/src/silx/math/fit/fittheories.py +++ b/src/silx/math/fit/fittheories.py @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # # Copyright (c) 2004-2021 European Synchrotron Radiation Facility diff --git a/src/silx/math/fit/fittheory.py b/src/silx/math/fit/fittheory.py index fa42e6b..ab3ae43 100644 --- a/src/silx/math/fit/fittheory.py +++ b/src/silx/math/fit/fittheory.py @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # # Copyright (c) 2004-2018 European Synchrotron Radiation Facility diff --git a/src/silx/math/fit/functions.pyx b/src/silx/math/fit/functions.pyx index 1f78563..a69086c 100644 --- a/src/silx/math/fit/functions.pyx +++ b/src/silx/math/fit/functions.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2016-2020 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/functions_wrapper.pxd b/src/silx/math/fit/functions_wrapper.pxd index 780116c..38de94a 100644 --- a/src/silx/math/fit/functions_wrapper.pxd +++ b/src/silx/math/fit/functions_wrapper.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/leastsq.py b/src/silx/math/fit/leastsq.py index 3df1a35..e49977f 100644 --- a/src/silx/math/fit/leastsq.py +++ b/src/silx/math/fit/leastsq.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2020 European Synchrotron Radiation Facility diff --git a/src/silx/math/fit/peaks.pyx b/src/silx/math/fit/peaks.pyx index a4fce89..cc705a5 100644 --- a/src/silx/math/fit/peaks.pyx +++ b/src/silx/math/fit/peaks.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # @@ -65,6 +64,7 @@ def peak_search(y, fwhm, sensitivity=3.5, if ``relevance_info`` is ``False``. Else, sequence of ``(peak_index, peak_relevance)`` tuples (one tuple per peak). + WARNING: Peak indices are returned as float64. :raise: ``IndexError`` if the number of peaks is too large to fit in the output array. """ diff --git a/src/silx/math/fit/peaks_wrapper.pxd b/src/silx/math/fit/peaks_wrapper.pxd index 4c77dc6..0ae1910 100644 --- a/src/silx/math/fit/peaks_wrapper.pxd +++ b/src/silx/math/fit/peaks_wrapper.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 #/*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/setup.py b/src/silx/math/fit/setup.py deleted file mode 100644 index 649387f..0000000 --- a/src/silx/math/fit/setup.py +++ /dev/null @@ -1,85 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-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. -# -# ############################################################################*/ - - -__authors__ = ["P. Knobel"] -__license__ = "MIT" -__date__ = "22/06/2016" - - -import os.path - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('fit', parent_package, top_path) - config.add_subpackage('test') - - # ===================================== - # fit functions - # ===================================== - fun_src = [os.path.join('functions', "src", "funs.c"), - "functions.pyx"] - fun_inc = [os.path.join('functions', 'include')] - - config.add_extension('functions', - sources=fun_src, - include_dirs=fun_inc, - language='c') - - # ===================================== - # fit filters - # ===================================== - filt_src = [os.path.join('filters', "src", srcf) - for srcf in ["smoothnd.c", "snip1d.c", - "snip2d.c", "snip3d.c", "strip.c"]] - filt_src.append("filters.pyx") - filt_inc = [os.path.join('filters', 'include')] - - config.add_extension('filters', - sources=filt_src, - include_dirs=filt_inc, - language='c') - - # ===================================== - # peaks - # ===================================== - peaks_src = [os.path.join('peaks', "src", "peaks.c"), - "peaks.pyx"] - peaks_inc = [os.path.join('peaks', 'include')] - - config.add_extension('peaks', - sources=peaks_src, - include_dirs=peaks_inc, - language='c') - # ===================================== - # ===================================== - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/math/fit/test/__init__.py b/src/silx/math/fit/test/__init__.py index 745efe3..bfc51f5 100644 --- a/src/silx/math/fit/test/__init__.py +++ b/src/silx/math/fit/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/test/test_bgtheories.py b/src/silx/math/fit/test/test_bgtheories.py index 6620d38..40f0831 100644 --- a/src/silx/math/fit/test/test_bgtheories.py +++ b/src/silx/math/fit/test/test_bgtheories.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/test/test_filters.py b/src/silx/math/fit/test/test_filters.py index 8314bdc..5b8b070 100644 --- a/src/silx/math/fit/test/test_filters.py +++ b/src/silx/math/fit/test/test_filters.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/test/test_fit.py b/src/silx/math/fit/test/test_fit.py index 00f04e2..39a04f9 100644 --- a/src/silx/math/fit/test/test_fit.py +++ b/src/silx/math/fit/test/test_fit.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/test/test_fitmanager.py b/src/silx/math/fit/test/test_fitmanager.py index 4ab56a5..cc35ccf 100644 --- a/src/silx/math/fit/test/test_fitmanager.py +++ b/src/silx/math/fit/test/test_fitmanager.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2020 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/test/test_functions.py b/src/silx/math/fit/test/test_functions.py index 7e3ff63..71cce8b 100644 --- a/src/silx/math/fit/test/test_functions.py +++ b/src/silx/math/fit/test/test_functions.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/fit/test/test_peaks.py b/src/silx/math/fit/test/test_peaks.py index 495c70d..23e4061 100644 --- a/src/silx/math/fit/test/test_peaks.py +++ b/src/silx/math/fit/test/test_peaks.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/histogram.py b/src/silx/math/histogram.py index af9ee68..e00daa9 100644 --- a/src/silx/math/histogram.py +++ b/src/silx/math/histogram.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/histogramnd/include/histogramnd_c.h b/src/silx/math/histogramnd/include/histogramnd_c.h index abe464f..25293b9 100644 --- a/src/silx/math/histogramnd/include/histogramnd_c.h +++ b/src/silx/math/histogramnd/include/histogramnd_c.h @@ -31,10 +31,10 @@ #else #include <inttypes.h> #endif - +#include <stddef.h> #include "templates.h" -/** Allowed flag values for the i_opt_flags arguments. +/** Allowed flag values for the i_opt_flags arguments. */ typedef enum { HISTO_NONE = 0, /**< No options. */ @@ -43,7 +43,7 @@ typedef enum { HISTO_LAST_BIN_CLOSED = 1<<2 /**< Last bin is closed. */ } histo_opt_type; -/** Return codees for the histogramnd function. +/** Return codees for the histogramnd function. */ typedef enum { HISTO_OK = 0, /**< No error. */ @@ -58,7 +58,7 @@ typedef enum { int histogramnd_double_double_double(double *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -67,11 +67,11 @@ int histogramnd_double_double_double(double *i_sample, int i_opt_flags, double i_weight_min, double i_weight_max); - + int histogramnd_double_float_double(double *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -80,11 +80,11 @@ int histogramnd_double_float_double(double *i_sample, int i_opt_flags, float i_weight_min, float i_weight_max); - + int histogramnd_double_int32_t_double(double *i_sample, int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -93,7 +93,7 @@ int histogramnd_double_int32_t_double(double *i_sample, int i_opt_flags, int32_t i_weight_min, int32_t i_weight_max); - + /*===================== * float sample, double cumul * ==================== @@ -101,7 +101,7 @@ int histogramnd_double_int32_t_double(double *i_sample, int histogramnd_float_double_double(float *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -110,11 +110,11 @@ int histogramnd_float_double_double(float *i_sample, int i_opt_flags, double i_weight_min, double i_weight_max); - + int histogramnd_float_float_double(float *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -123,11 +123,11 @@ int histogramnd_float_float_double(float *i_sample, int i_opt_flags, float i_weight_min, float i_weight_max); - + int histogramnd_float_int32_t_double(float *i_sample, int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -144,7 +144,7 @@ int histogramnd_float_int32_t_double(float *i_sample, int histogramnd_int32_t_double_double(int32_t *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -153,11 +153,11 @@ int histogramnd_int32_t_double_double(int32_t *i_sample, int i_opt_flags, double i_weight_min, double i_weight_max); - + int histogramnd_int32_t_float_double(int32_t *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -166,11 +166,11 @@ int histogramnd_int32_t_float_double(int32_t *i_sample, int i_opt_flags, float i_weight_min, float i_weight_max); - + int histogramnd_int32_t_int32_t_double(int32_t *i_sample, int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -179,7 +179,7 @@ int histogramnd_int32_t_int32_t_double(int32_t *i_sample, int i_opt_flags, int32_t i_weight_min, int32_t i_weight_max); - + /*===================== * double sample, float cumul * ==================== @@ -188,7 +188,7 @@ int histogramnd_int32_t_int32_t_double(int32_t *i_sample, int histogramnd_double_double_float(double *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -197,11 +197,11 @@ int histogramnd_double_double_float(double *i_sample, int i_opt_flags, double i_weight_min, double i_weight_max); - + int histogramnd_double_float_float(double *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -210,11 +210,11 @@ int histogramnd_double_float_float(double *i_sample, int i_opt_flags, float i_weight_min, float i_weight_max); - + int histogramnd_double_int32_t_float(double *i_sample, int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -223,7 +223,7 @@ int histogramnd_double_int32_t_float(double *i_sample, int i_opt_flags, int32_t i_weight_min, int32_t i_weight_max); - + /*===================== * float sample, float cumul * ==================== @@ -231,7 +231,7 @@ int histogramnd_double_int32_t_float(double *i_sample, int histogramnd_float_double_float(float *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -240,11 +240,11 @@ int histogramnd_float_double_float(float *i_sample, int i_opt_flags, double i_weight_min, double i_weight_max); - + int histogramnd_float_float_float(float *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -253,11 +253,11 @@ int histogramnd_float_float_float(float *i_sample, int i_opt_flags, float i_weight_min, float i_weight_max); - + int histogramnd_float_int32_t_float(float *i_sample, int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -274,7 +274,7 @@ int histogramnd_float_int32_t_float(float *i_sample, int histogramnd_int32_t_double_float(int32_t *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -283,11 +283,11 @@ int histogramnd_int32_t_double_float(int32_t *i_sample, int i_opt_flags, double i_weight_min, double i_weight_max); - + int histogramnd_int32_t_float_float(int32_t *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -296,11 +296,11 @@ int histogramnd_int32_t_float_float(int32_t *i_sample, int i_opt_flags, float i_weight_min, float i_weight_max); - + int histogramnd_int32_t_int32_t_float(int32_t *i_sample, int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, uint32_t *o_histo, @@ -309,5 +309,5 @@ int histogramnd_int32_t_int32_t_float(int32_t *i_sample, int i_opt_flags, int32_t i_weight_min, int32_t i_weight_max); - + #endif /* #define HISTOGRAMND_C_H */ diff --git a/src/silx/math/histogramnd/src/histogramnd_template.c b/src/silx/math/histogramnd/src/histogramnd_template.c index 0276bb4..e446b74 100644 --- a/src/silx/math/histogramnd/src/histogramnd_template.c +++ b/src/silx/math/histogramnd/src/histogramnd_template.c @@ -36,7 +36,7 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) (HISTO_SAMPLE_T *i_sample, HISTO_WEIGHT_T *i_weights, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bins, uint32_t *o_histo, @@ -48,43 +48,43 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) { /* some counters */ int i = 0, j = 0; - long elem_idx = 0; - + size_t elem_idx = 0; + HISTO_WEIGHT_T * weight_ptr = 0; HISTO_SAMPLE_T elem_coord = 0.; - + /* computed bin index (i_sample -> grid) */ long bin_idx = 0; - + double * g_min = 0; double * g_max = 0; double * range = 0; - + /* ================================ * Parsing options, if any. * ================================ */ - + int filt_min_weight = 0; int filt_max_weight = 0; int last_bin_closed = 0; - + /* Testing the option flags */ if(i_opt_flags & HISTO_WEIGHT_MIN) { filt_min_weight = 1; } - + if(i_opt_flags & HISTO_WEIGHT_MAX) { filt_max_weight = 1; } - + if(i_opt_flags & HISTO_LAST_BIN_CLOSED) { last_bin_closed = 1; } - + /* storing the min & max bin coordinates in their own arrays because * i_bin_ranges = [[min0, max0], [min1, max1], ...] * (mostly for the sake of clarity) @@ -94,7 +94,7 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) g_max = (double *) malloc(i_n_dim * sizeof(double)); /* range used to convert from i_coords to bin indices in the grid */ range = (double *) malloc(i_n_dim * sizeof(double)); - + if(!g_min || !g_max || !range) { free(g_min); @@ -102,14 +102,14 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) free(range); return HISTO_ERR_ALLOC; } - + j = 0; for(i=0; i<i_n_dim; i++) { g_min[i] = i_bin_ranges[i*2]; g_max[i] = i_bin_ranges[i*2+1]; range[i] = g_max[i]-g_min[i]; - + for(bin_idx=0; bin_idx<i_n_bins[i]; j++, bin_idx++) { o_bin_edges[j] = g_min[i] + @@ -117,9 +117,9 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) } o_bin_edges[j++] = g_max[i]; } - + weight_ptr = i_weights; - + if(!i_weights) { /* if weights are not provided there no point in trying to filter them @@ -127,7 +127,7 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) */ filt_min_weight = 0; filt_max_weight = 0; - + /* If the weights array is not provided then there is no point * updating the weighted histogram, only the bin counts (o_histo) * will be filled. @@ -135,9 +135,9 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) */ o_cumul = 0; } - + /* tried to use pointers instead of indices here, but it didn't - * seem any faster (probably because the compiler + * seem any faster (probably because the compiler * optimizes stuff anyway), * so i'm keeping the "indices" version, for the sake of clarity */ @@ -159,11 +159,11 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) } bin_idx = 0; - + for(i=0; i<i_n_dim; i++) { elem_coord = i_sample[elem_idx+i]; - + /* ===================== * Element is rejected if any of the following is NOT true : * 1. coordinate is >= than the minimum value @@ -176,7 +176,7 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) bin_idx = -1; break; } - + /* Here we make the assumption that most of the time * there will be more coordinates inside the grid interval * (one test) @@ -193,7 +193,7 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) * i_n_bins[i] * ); */ - + /* Not using floor to speed up things. * We don't (?) need all the error checking provided by * the built-in floor(). @@ -221,33 +221,33 @@ int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T) break; } } /* if(elem_coord<g_max[i]) */ - + } /* for(i=0; i<i_n_dim; i++) */ - + /* element is out of the grid */ if(bin_idx==-1) { continue; } - + if(o_histo) { o_histo[bin_idx] += 1; } if(o_cumul) { - /* not testing the pointer since o_cumul is null if - * i_weights is null. + /* not testing the pointer since o_cumul is null if + * i_weights is null. */ o_cumul[bin_idx] += (HISTO_CUMUL_T) *weight_ptr; } - + } /* for(elem_idx=0; elem_idx<i_n_elem*i_n_dim; elem_idx+=i_n_dim) */ - + free(g_min); free(g_max); free(range); - + /* For now just returning 0 (OK) since all the checks are done in * python. This might change later if people want to call this * function directly from C (might have to implement error codes). diff --git a/src/silx/math/histogramnd_c.pxd b/src/silx/math/histogramnd_c.pxd index 35db529..54a3daf 100644 --- a/src/silx/math/histogramnd_c.pxd +++ b/src/silx/math/histogramnd_c.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # @@ -47,7 +46,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_double_double_double(double *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -60,7 +59,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_double_float_double(double *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -73,7 +72,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_double_int32_t_double(double *i_sample, cnumpy.int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -90,7 +89,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_float_double_double(float *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -103,7 +102,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_float_float_double(float *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -116,7 +115,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_float_int32_t_double(float *i_sample, cnumpy.int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -133,7 +132,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_int32_t_double_double(cnumpy.int32_t *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -146,7 +145,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_int32_t_float_double(cnumpy.int32_t *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -159,7 +158,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_int32_t_int32_t_double(cnumpy.int32_t *i_sample, cnumpy.int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -176,7 +175,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_double_double_float(double *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -189,7 +188,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_double_float_float(double *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -202,7 +201,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_double_int32_t_float(double *i_sample, cnumpy.int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -219,7 +218,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_float_double_float(float *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -232,7 +231,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_float_float_float(float *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -245,7 +244,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_float_int32_t_float(float *i_sample, cnumpy.int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -262,7 +261,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_int32_t_double_float(cnumpy.int32_t *i_sample, double *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -275,7 +274,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_int32_t_float_float(cnumpy.int32_t *i_sample, float *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, @@ -288,7 +287,7 @@ cdef extern from "histogramnd_c.h": int histogramnd_int32_t_int32_t_float(cnumpy.int32_t *i_sample, cnumpy.int32_t *i_weigths, int i_n_dim, - int i_n_elem, + size_t i_n_elem, double *i_bin_ranges, int *i_n_bin, cnumpy.uint32_t *o_histo, diff --git a/src/silx/math/interpolate.pyx b/src/silx/math/interpolate.pyx index c79224a..948cf9a 100644 --- a/src/silx/math/interpolate.pyx +++ b/src/silx/math/interpolate.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility diff --git a/src/silx/math/marchingcubes.pyx b/src/silx/math/marchingcubes.pyx index 0409691..78b76c4 100644 --- a/src/silx/math/marchingcubes.pyx +++ b/src/silx/math/marchingcubes.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility diff --git a/src/silx/math/math_compatibility.pxd b/src/silx/math/math_compatibility.pxd index ddaa550..98fd21d 100644 --- a/src/silx/math/math_compatibility.pxd +++ b/src/silx/math/math_compatibility.pxd @@ -1,4 +1,3 @@ -# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2018 European Synchrotron Radiation Facility
diff --git a/src/silx/math/mc.pxd b/src/silx/math/mc.pxd index b1c81e7..6270e21 100644 --- a/src/silx/math/mc.pxd +++ b/src/silx/math/mc.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2016 European Synchrotron Radiation Facility diff --git a/src/silx/math/medianfilter/__init__.py b/src/silx/math/medianfilter/__init__.py index 2b05f06..5c199e3 100644 --- a/src/silx/math/medianfilter/__init__.py +++ b/src/silx/math/medianfilter/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/medianfilter/median_filter.pxd b/src/silx/math/medianfilter/median_filter.pxd index 2fc0283..b302388 100644 --- a/src/silx/math/medianfilter/median_filter.pxd +++ b/src/silx/math/medianfilter/median_filter.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility diff --git a/src/silx/math/medianfilter/medianfilter.pyx b/src/silx/math/medianfilter/medianfilter.pyx index fe05a78..f4b7c51 100644 --- a/src/silx/math/medianfilter/medianfilter.pyx +++ b/src/silx/math/medianfilter/medianfilter.pyx @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility diff --git a/src/silx/math/medianfilter/setup.py b/src/silx/math/medianfilter/setup.py deleted file mode 100644 index d228357..0000000 --- a/src/silx/math/medianfilter/setup.py +++ /dev/null @@ -1,59 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["D. Naudet"] -__license__ = "MIT" -__date__ = "02/05/2017" - - -import numpy - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('medianfilter', parent_package, top_path) - config.add_subpackage('test') - - # ===================================== - # median filter - # ===================================== - medfilt_src = ['medianfilter.pyx'] - medfilt_inc = ['include', numpy.get_include()] - extra_link_args = ['-fopenmp'] - extra_compile_args = ['-fopenmp'] - config.add_extension('medianfilter', - sources=medfilt_src, - include_dirs=[medfilt_inc], - language='c++', - extra_link_args=extra_link_args, - extra_compile_args=extra_compile_args) - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration)
\ No newline at end of file diff --git a/src/silx/math/medianfilter/test/__init__.py b/src/silx/math/medianfilter/test/__init__.py index 71f8e95..8bcf19a 100644 --- a/src/silx/math/medianfilter/test/__init__.py +++ b/src/silx/math/medianfilter/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/math/medianfilter/test/benchmark.py b/src/silx/math/medianfilter/test/benchmark.py index 81e893e..ebe4ac4 100644 --- a/src/silx/math/medianfilter/test/benchmark.py +++ b/src/silx/math/medianfilter/test/benchmark.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2017-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/math/medianfilter/test/test_medianfilter.py b/src/silx/math/medianfilter/test/test_medianfilter.py index a4e3021..15ee92e 100644 --- a/src/silx/math/medianfilter/test/test_medianfilter.py +++ b/src/silx/math/medianfilter/test/test_medianfilter.py @@ -1,4 +1,3 @@ -# coding: utf-8 # ########################################################################## # Copyright (C) 2017-2018 European Synchrotron Radiation Facility # diff --git a/src/silx/math/setup.py b/src/silx/math/setup.py deleted file mode 100644 index 1c30e6e..0000000 --- a/src/silx/math/setup.py +++ /dev/null @@ -1,99 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-2021 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################*/ - -__authors__ = ["D. Naudet"] -__license__ = "MIT" -__date__ = "27/03/2017" - -import os.path - -import numpy - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('math', parent_package, top_path) - config.add_subpackage('test') - config.add_subpackage('fit') - config.add_subpackage('medianfilter') - config.add_subpackage('fft') - - # ===================================== - # histogramnd - # ===================================== - histo_src = [os.path.join('histogramnd', 'src', 'histogramnd_c.c'), - 'chistogramnd.pyx'] - histo_inc = [os.path.join('histogramnd', 'include'), - numpy.get_include()] - - config.add_extension('chistogramnd', - sources=histo_src, - include_dirs=histo_inc, - language='c') - - # ===================================== - # histogramnd_lut - # ===================================== - config.add_extension('chistogramnd_lut', - sources=['chistogramnd_lut.pyx'], - include_dirs=histo_inc, - language='c') - # ===================================== - # marching cubes - # ===================================== - mc_src = [os.path.join('marchingcubes', 'mc_lut.cpp'), - 'marchingcubes.pyx'] - config.add_extension('marchingcubes', - sources=mc_src, - include_dirs=['marchingcubes', numpy.get_include()], - language='c++') - - # min/max - config.add_extension('combo', - sources=['combo.pyx'], - include_dirs=['include'], - language='c') - - config.add_extension('_colormap', - sources=["_colormap.pyx"], - language='c', - include_dirs=['include', numpy.get_include()], - extra_link_args=['-fopenmp'], - extra_compile_args=['-fopenmp']) - - config.add_extension('interpolate', - sources=["interpolate.pyx"], - language='c', - include_dirs=['include', numpy.get_include()], - extra_link_args=['-fopenmp'], - extra_compile_args=['-fopenmp']) - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/math/test/__init__.py b/src/silx/math/test/__init__.py index ad9836c..d076ee3 100644 --- a/src/silx/math/test/__init__.py +++ b/src/silx/math/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2019 European Synchrotron Radiation Facility # diff --git a/src/silx/math/test/benchmark_combo.py b/src/silx/math/test/benchmark_combo.py index c12f590..484bc93 100644 --- a/src/silx/math/test/benchmark_combo.py +++ b/src/silx/math/test/benchmark_combo.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # @@ -23,8 +22,6 @@ # ############################################################################*/ """Benchmarks of the combo module""" -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/01/2018" diff --git a/src/silx/math/test/histo_benchmarks.py b/src/silx/math/test/histo_benchmarks.py index 7d3216d..6cc5507 100644 --- a/src/silx/math/test/histo_benchmarks.py +++ b/src/silx/math/test/histo_benchmarks.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/test/test_HistogramndLut_nominal.py b/src/silx/math/test/test_HistogramndLut_nominal.py index 52e003c..907a592 100644 --- a/src/silx/math/test/test_HistogramndLut_nominal.py +++ b/src/silx/math/test/test_HistogramndLut_nominal.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2019 European Synchrotron Radiation Facility # diff --git a/src/silx/math/test/test_calibration.py b/src/silx/math/test/test_calibration.py index 7158293..1c961be 100644 --- a/src/silx/math/test/test_calibration.py +++ b/src/silx/math/test/test_calibration.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2018 European Synchrotron Radiation Facility # @@ -23,8 +22,6 @@ # ############################################################################*/ """Tests of the calibration module""" -from __future__ import division - __authors__ = ["P. Knobel"] __license__ = "MIT" __date__ = "14/05/2018" diff --git a/src/silx/math/test/test_colormap.py b/src/silx/math/test/test_colormap.py index 0b0ec59..144ee5f 100644 --- a/src/silx/math/test/test_colormap.py +++ b/src/silx/math/test/test_colormap.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2021 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ############################################################################*/ """Test for colormap mapping implementation""" -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "16/05/2018" diff --git a/src/silx/math/test/test_combo.py b/src/silx/math/test/test_combo.py index 9a96923..eed0625 100644 --- a/src/silx/math/test/test_combo.py +++ b/src/silx/math/test/test_combo.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2020 European Synchrotron Radiation Facility # @@ -23,8 +22,6 @@ # ############################################################################*/ """Tests of the combo module""" -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/01/2018" diff --git a/src/silx/math/test/test_histogramnd_error.py b/src/silx/math/test/test_histogramnd_error.py index 22304cb..d01cab9 100644 --- a/src/silx/math/test/test_histogramnd_error.py +++ b/src/silx/math/test/test_histogramnd_error.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # diff --git a/src/silx/math/test/test_histogramnd_nominal.py b/src/silx/math/test/test_histogramnd_nominal.py index 031a772..9a8c3c3 100644 --- a/src/silx/math/test/test_histogramnd_nominal.py +++ b/src/silx/math/test/test_histogramnd_nominal.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/math/test/test_histogramnd_vs_np.py b/src/silx/math/test/test_histogramnd_vs_np.py index d6a8d19..d1fb8be 100644 --- a/src/silx/math/test/test_histogramnd_vs_np.py +++ b/src/silx/math/test/test_histogramnd_vs_np.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # @@ -26,8 +25,10 @@ Tests for the histogramnd function. Results are compared to numpy's histogramdd. """ +import os import unittest import operator +import pytest import numpy as np @@ -609,6 +610,40 @@ class _TestHistogramnd(unittest.TestCase): result_np_w_1[0].sum(dtype=np.float64)), msg=self.state_msg) + @pytest.mark.usefixtures("use_large_memory") + def test_histo_big_array(self): + """ + Test histogram on arrays with more than 2**31-1 samples. + """ + if self.sample.ndim > 1: + self.skipTest("Test only many samples along one dimension") + if self.sample.dtype.itemsize > 4: + self.skipTest("Test only many samples for itemsize < 4") + n_repeat = (2**31 + 10) // self.sample.size + sample = np.repeat(self.sample, n_repeat) + n_bins = int(1e6) + result_c = histogramnd( + sample, + self.histo_range, + n_bins, + last_bin_closed=True + ) + result_np = np.histogramdd( + sample, + n_bins, + range=self.histo_range + ) + for i_edges, edges in enumerate(result_c[2]): + self.assertTrue( + np.allclose( + edges, + result_np[1][i_edges] + ), + msg='{0}. Testing bin_edges for dim {1}.'''.format(self.state_msg, i_edges+1) + ) + + + class _TestHistogramnd_1d(_TestHistogramnd): """ diff --git a/src/silx/math/test/test_interpolate.py b/src/silx/math/test/test_interpolate.py index 146449d..cff8bd9 100644 --- a/src/silx/math/test/test_interpolate.py +++ b/src/silx/math/test/test_interpolate.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility diff --git a/src/silx/math/test/test_marchingcubes.py b/src/silx/math/test/test_marchingcubes.py index 5e2b193..7c60414 100644 --- a/src/silx/math/test/test_marchingcubes.py +++ b/src/silx/math/test/test_marchingcubes.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016 European Synchrotron Radiation Facility # @@ -23,8 +22,6 @@ # ############################################################################*/ """Tests of the marchingcubes module""" -from __future__ import division - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "17/01/2018" diff --git a/src/silx/opencl/__init__.py b/src/silx/opencl/__init__.py index fbd1f88..466ffaf 100644 --- a/src/silx/opencl/__init__.py +++ b/src/silx/opencl/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: S I L X project # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/backprojection.py b/src/silx/opencl/backprojection.py index 65a9836..9f747c1 100644 --- a/src/silx/opencl/backprojection.py +++ b/src/silx/opencl/backprojection.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for (filtered) backprojection on the GPU""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["A. Mirone, P. Paleo"] __license__ = "MIT" __date__ = "25/01/2019" diff --git a/src/silx/opencl/codec/byte_offset.py b/src/silx/opencl/codec/byte_offset.py index 9a52427..e497a73 100644 --- a/src/silx/opencl/codec/byte_offset.py +++ b/src/silx/opencl/codec/byte_offset.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: Sift implementation in Python + OpenCL # https://github.com/silx-kit/silx @@ -31,8 +30,6 @@ This module provides a class for CBF byte offset compression/decompression. """ -from __future__ import division, print_function, with_statement - __authors__ = ["Jérôme Kieffer"] __contact__ = "jerome.kieffer@esrf.eu" __license__ = "MIT" diff --git a/src/silx/opencl/codec/setup.py b/src/silx/opencl/codec/setup.py deleted file mode 100644 index 4a5c1e5..0000000 --- a/src/silx/opencl/codec/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding: utf-8 -# -# Copyright (C) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - -from __future__ import division - -__contact__ = "jerome.kieffer@esrf.eu" -__license__ = "MIT" -__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -__authors__ = ["J. Kieffer"] -__date__ = "13/10/2017" - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('codec', parent_package, top_path) - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/opencl/codec/test/__init__.py b/src/silx/opencl/codec/test/__init__.py index 325c2c7..45065f8 100644 --- a/src/silx/opencl/codec/test/__init__.py +++ b/src/silx/opencl/codec/test/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/codec/test/test_byte_offset.py b/src/silx/opencl/codec/test/test_byte_offset.py index 4b2d5a3..9ed53bc 100644 --- a/src/silx/opencl/codec/test/test_byte_offset.py +++ b/src/silx/opencl/codec/test/test_byte_offset.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: Byte-offset decompression in OpenCL # https://github.com/silx-kit/silx @@ -31,8 +30,6 @@ Test suite for byte-offset decompression """ -from __future__ import division, print_function - __authors__ = ["Jérôme Kieffer"] __contact__ = "jerome.kieffer@esrf.eu" __license__ = "MIT" diff --git a/src/silx/opencl/common.py b/src/silx/opencl/common.py index 60849d6..cf51406 100644 --- a/src/silx/opencl/common.py +++ b/src/silx/opencl/common.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: S I L X project # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/convolution.py b/src/silx/opencl/convolution.py index 15ef931..481e8fb 100644 --- a/src/silx/opencl/convolution.py +++ b/src/silx/opencl/convolution.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for convolution on CPU/GPU.""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["P. Paleo"] __license__ = "MIT" __date__ = "01/08/2019" diff --git a/src/silx/opencl/image.py b/src/silx/opencl/image.py index 65e2d5e..6a4a854 100644 --- a/src/silx/opencl/image.py +++ b/src/silx/opencl/image.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx @@ -28,8 +27,6 @@ """A general purpose library for manipulating 2D images in 1 or 3 colors """ -from __future__ import absolute_import, print_function, with_statement, division - __author__ = "Jerome Kieffer" __license__ = "MIT" diff --git a/src/silx/opencl/linalg.py b/src/silx/opencl/linalg.py index a64122a..77d826b 100644 --- a/src/silx/opencl/linalg.py +++ b/src/silx/opencl/linalg.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for basic linear algebra in OpenCL""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["P. Paleo"] __license__ = "MIT" __date__ = "01/08/2019" diff --git a/src/silx/opencl/medfilt.py b/src/silx/opencl/medfilt.py index d4e425b..ae63eb2 100644 --- a/src/silx/opencl/medfilt.py +++ b/src/silx/opencl/medfilt.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: Azimuthal integration # https://github.com/silx-kit/pyFAI @@ -31,8 +30,6 @@ The target is to mimic the signature of scipy.signal.medfilt and scipy.medfilt2 The first implementation targets 2D implementation where this operation is costly (~10s/2kx2k image) """ -from __future__ import absolute_import, print_function, with_statement, division - __author__ = "Jerome Kieffer" __license__ = "MIT" diff --git a/src/silx/opencl/processing.py b/src/silx/opencl/processing.py index 8b81f7f..c223354 100644 --- a/src/silx/opencl/processing.py +++ b/src/silx/opencl/processing.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: S I L X project # https://github.com/silx-kit/silx @@ -38,7 +37,7 @@ __author__ = "Jerome Kieffer" __contact__ = "Jerome.Kieffer@ESRF.eu" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -__date__ = "02/03/2021" +__date__ = "06/10/2022" __status__ = "stable" import sys @@ -53,7 +52,8 @@ from .utils import concatenate_cl_kernel import platform BufferDescription = namedtuple("BufferDescription", ["name", "size", "dtype", "flags"]) -EventDescription = namedtuple("EventDescription", ["name", "event"]) +EventDescription = namedtuple("EventDescription", ["name", "event"]) # Deprecated, please use ProfileDescription +ProfileDescription = namedtuple("ProfileDescription", ["name", "start", "stop"]) logger = logging.getLogger(__name__) @@ -286,6 +286,7 @@ class OpenclProcessing(object): self.kernels = None self.program = None +# Methods about Profiling def set_profiling(self, value=True): """Switch On/Off the profiling flag of the command queue to allow debugging @@ -309,37 +310,42 @@ class OpenclProcessing(object): """ Add an OpenCL event to the events lists, if profiling is enabled. - :param event: silx.opencl.processing.EventDescription. + :param event: pyopencl.NanyEvent. :param desc: event description """ if self.profile: - self.events.append(EventDescription(desc, event)) - - def allocate_texture(self, shape, hostbuf=None, support_1D=False): - return allocate_texture(self.ctx, shape, hostbuf=hostbuf, support_1D=support_1D) + try: + profile = event.profile + self.events.append(ProfileDescription(desc, profile.start, profile.end)) + except Exception: + # Probably the driver does not support profiling + pass - def transfer_to_texture(self, arr, tex_ref): + def profile_multi(self, event_lists): """ - Transfer an array to a texture. + Extract profiling info from several OpenCL event, if profiling is enabled. - :param arr: Input array. Can be a numpy array or a pyopencl array. - :param tex_ref: texture reference (pyopencl._cl.Image). + :param event_lists: list of ("desc", pyopencl.NanyEvent). """ - copy_args = [self.queue, tex_ref, arr] - shp = arr.shape - ndim = arr.ndim - if ndim == 1: - # pyopencl and OpenCL < 1.2 do not support image1d_t - # force 2D with one row in this case - # ~ ndim = 2 - shp = (1,) + shp - copy_kwargs = {"origin":(0,) * ndim, "region": shp[::-1]} - if not(isinstance(arr, numpy.ndarray)): # assuming pyopencl.array.Array - # D->D copy - copy_args[2] = arr.data - copy_kwargs["offset"] = 0 - ev = pyopencl.enqueue_copy(*copy_args, **copy_kwargs) - self.profile_add(ev, "Transfer to texture") + if self.profile: + for event_desc in event_lists: + if isinstance(event_desc, ProfileDescription): + self.events.append(event_desc) + else: + if isinstance(event_desc, EventDescription) or "__len__" in dir(e) and len(e) == 2: + desc, event = event_desc + else: + desc = "?" + event = event_desc + try: + profile = event.profile + start = profile.start + end = profile.end + except Exception: + # probably an unfinished job ... use old-style. + self.events.append(event_desc) + else: + self.events.append(ProfileDescription(desc, start, end)) def log_profile(self, stats=False): """If we are in profiling mode, prints out all timing for every single OpenCL call @@ -359,20 +365,29 @@ class OpenclProcessing(object): if self.profile: for e in self.events: - if "__len__" in dir(e) and len(e) >= 2: + if isinstance(e, ProfileDescription): + name = e[0] + t0 = e[1] + t1 = e[2] + elif isinstance(e, EventDescription) or "__len__" in dir(e) and len(e) == 2: name = e[0] pr = e[1].profile t0 = pr.start t1 = pr.end - et = 1e-6 * (t1 - t0) - total_time += et - if stats is None: - out.append(f"{name:>50} : {et:.3f}ms") + else: + name = "?" + t0 = e.profile.start + t1 = e.profile.end + + et = 1e-6 * (t1 - t0) + total_time += et + if stats is None: + out.append(f"{name:>50} : {et:.3f}ms") + else: + if name in stats: + stats[name].append(et) else: - if name in stats: - stats[name].append(et) - else: - stats[name] = [et] + stats[name] = [et] if stats is not None: for k, v in stats.items(): n = numpy.array(v) @@ -390,6 +405,33 @@ class OpenclProcessing(object): with self.sem: self.events = [] +# Methods about textures + def allocate_texture(self, shape, hostbuf=None, support_1D=False): + return allocate_texture(self.ctx, shape, hostbuf=hostbuf, support_1D=support_1D) + + def transfer_to_texture(self, arr, tex_ref): + """ + Transfer an array to a texture. + + :param arr: Input array. Can be a numpy array or a pyopencl array. + :param tex_ref: texture reference (pyopencl._cl.Image). + """ + copy_args = [self.queue, tex_ref, arr] + shp = arr.shape + ndim = arr.ndim + if ndim == 1: + # pyopencl and OpenCL < 1.2 do not support image1d_t + # force 2D with one row in this case + # ~ ndim = 2 + shp = (1,) + shp + copy_kwargs = {"origin":(0,) * ndim, "region": shp[::-1]} + if not(isinstance(arr, numpy.ndarray)): # assuming pyopencl.array.Array + # D->D copy + copy_args[2] = arr.data + copy_kwargs["offset"] = 0 + ev = pyopencl.enqueue_copy(*copy_args, **copy_kwargs) + self.profile_add(ev, "Transfer to texture") + @property def x87_volatile_option(self): # this is running 32 bits OpenCL woth POCL diff --git a/src/silx/opencl/projection.py b/src/silx/opencl/projection.py index c02faf6..a02e28b 100644 --- a/src/silx/opencl/projection.py +++ b/src/silx/opencl/projection.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2020 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for tomographic projector on the GPU""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["A. Mirone, P. Paleo"] __license__ = "MIT" __date__ = "01/08/2019" diff --git a/src/silx/opencl/reconstruction.py b/src/silx/opencl/reconstruction.py index 2c84aee..c85fd42 100644 --- a/src/silx/opencl/reconstruction.py +++ b/src/silx/opencl/reconstruction.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for tomographic reconstruction algorithms""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["P. Paleo"] __license__ = "MIT" __date__ = "01/08/2019" diff --git a/src/silx/opencl/setup.py b/src/silx/opencl/setup.py deleted file mode 100644 index 10fb1be..0000000 --- a/src/silx/opencl/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -# coding: utf-8 -# -# Copyright (C) 2016-2017 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - -from __future__ import division - -__contact__ = "jerome.kieffer@esrf.eu" -__license__ = "MIT" -__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -__authors__ = ["J. Kieffer"] -__date__ = "16/10/2017" - -import os.path -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('opencl', parent_package, top_path) - path = os.path.dirname(os.path.abspath(__file__)) - if os.path.exists(os.path.join(path, 'sift')): - config.add_subpackage('sift') - config.add_subpackage('codec') - config.add_subpackage('test') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/opencl/sinofilter.py b/src/silx/opencl/sinofilter.py index d608744..890267e 100644 --- a/src/silx/opencl/sinofilter.py +++ b/src/silx/opencl/sinofilter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for sinogram filtering on CPU/GPU.""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["P. Paleo"] __license__ = "MIT" __date__ = "07/06/2019" diff --git a/src/silx/opencl/sparse.py b/src/silx/opencl/sparse.py index 514589a..709e3c7 100644 --- a/src/silx/opencl/sparse.py +++ b/src/silx/opencl/sparse.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Module for data sparsification on CPU/GPU.""" -from __future__ import absolute_import, print_function, with_statement, division - __authors__ = ["P. Paleo"] __license__ = "MIT" __date__ = "07/06/2019" diff --git a/src/silx/opencl/statistics.py b/src/silx/opencl/statistics.py index a96ee33..9197dd1 100644 --- a/src/silx/opencl/statistics.py +++ b/src/silx/opencl/statistics.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: SILX # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/test/__init__.py b/src/silx/opencl/test/__init__.py index 92cda4a..b1ecf1b 100644 --- a/src/silx/opencl/test/__init__.py +++ b/src/silx/opencl/test/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Project: silx # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/test/test_addition.py b/src/silx/opencl/test/test_addition.py index 3b668bf..d6cf1ac 100644 --- a/src/silx/opencl/test/test_addition.py +++ b/src/silx/opencl/test/test_addition.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: Sift implementation in Python + OpenCL # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/test/test_array_utils.py b/src/silx/opencl/test/test_array_utils.py index 325a6c3..125d323 100644 --- a/src/silx/opencl/test/test_array_utils.py +++ b/src/silx/opencl/test/test_array_utils.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Test of the OpenCL array_utils""" -from __future__ import division, print_function - __authors__ = ["Pierre paleo"] __license__ = "MIT" __copyright__ = "2013-2017 European Synchrotron Radiation Facility, Grenoble, France" diff --git a/src/silx/opencl/test/test_backprojection.py b/src/silx/opencl/test/test_backprojection.py index 96d56fa..501cf2f 100644 --- a/src/silx/opencl/test/test_backprojection.py +++ b/src/silx/opencl/test/test_backprojection.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Test of the filtered backprojection module""" -from __future__ import division, print_function - __authors__ = ["Pierre paleo"] __license__ = "MIT" __copyright__ = "2013-2017 European Synchrotron Radiation Facility, Grenoble, France" diff --git a/src/silx/opencl/test/test_convolution.py b/src/silx/opencl/test/test_convolution.py index 6a2759d..e38a36a 100644 --- a/src/silx/opencl/test/test_convolution.py +++ b/src/silx/opencl/test/test_convolution.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -28,8 +27,6 @@ Test of the Convolution class. """ -from __future__ import division, print_function - __authors__ = ["Pierre Paleo"] __contact__ = "pierre.paleo@esrf.fr" __license__ = "MIT" diff --git a/src/silx/opencl/test/test_doubleword.py b/src/silx/opencl/test/test_doubleword.py index a33cf5a..8ab594d 100644 --- a/src/silx/opencl/test/test_doubleword.py +++ b/src/silx/opencl/test/test_doubleword.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # # Project: The silx project # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/test/test_image.py b/src/silx/opencl/test/test_image.py index 73c771b..4ea8960 100644 --- a/src/silx/opencl/test/test_image.py +++ b/src/silx/opencl/test/test_image.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: image manipulation in OpenCL # https://github.com/silx-kit/silx @@ -29,8 +28,6 @@ Simple test of image manipulation """ -from __future__ import division, print_function - __authors__ = ["Jérôme Kieffer"] __contact__ = "jerome.kieffer@esrf.eu" __license__ = "MIT" diff --git a/src/silx/opencl/test/test_kahan.py b/src/silx/opencl/test/test_kahan.py index 9e4a1e3..62ed047 100644 --- a/src/silx/opencl/test/test_kahan.py +++ b/src/silx/opencl/test/test_kahan.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # # Project: OpenCL numerical library # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/test/test_linalg.py b/src/silx/opencl/test/test_linalg.py index a997a36..da99480 100644 --- a/src/silx/opencl/test/test_linalg.py +++ b/src/silx/opencl/test/test_linalg.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Test of the linalg module""" -from __future__ import division, print_function - __authors__ = ["Pierre paleo"] __license__ = "MIT" __copyright__ = "2013-2017 European Synchrotron Radiation Facility, Grenoble, France" diff --git a/src/silx/opencl/test/test_medfilt.py b/src/silx/opencl/test/test_medfilt.py index 339e0f2..e657d0d 100644 --- a/src/silx/opencl/test/test_medfilt.py +++ b/src/silx/opencl/test/test_medfilt.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: Median filter of images + OpenCL # https://github.com/silx-kit/silx @@ -29,8 +28,6 @@ Simple test of the median filter """ -from __future__ import division, print_function - __authors__ = ["Jérôme Kieffer"] __contact__ = "jerome.kieffer@esrf.eu" __license__ = "MIT" diff --git a/src/silx/opencl/test/test_projection.py b/src/silx/opencl/test/test_projection.py index 13db5f4..d093e4b 100644 --- a/src/silx/opencl/test/test_projection.py +++ b/src/silx/opencl/test/test_projection.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility @@ -25,8 +24,6 @@ # ###########################################################################*/ """Test of the forward projection module""" -from __future__ import division, print_function - __authors__ = ["Pierre paleo"] __license__ = "MIT" __copyright__ = "2013-2017 European Synchrotron Radiation Facility, Grenoble, France" diff --git a/src/silx/opencl/test/test_sparse.py b/src/silx/opencl/test/test_sparse.py index 1d26b36..62a1399 100644 --- a/src/silx/opencl/test/test_sparse.py +++ b/src/silx/opencl/test/test_sparse.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility diff --git a/src/silx/opencl/test/test_stats.py b/src/silx/opencl/test/test_stats.py index 859271d..f8ab1a7 100644 --- a/src/silx/opencl/test/test_stats.py +++ b/src/silx/opencl/test/test_stats.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Project: Sift implementation in Python + OpenCL # https://github.com/silx-kit/silx diff --git a/src/silx/opencl/utils.py b/src/silx/opencl/utils.py index 575e018..cc9f62d 100644 --- a/src/silx/opencl/utils.py +++ b/src/silx/opencl/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # /*########################################################################## # Copyright (C) 2017 European Synchrotron Radiation Facility # @@ -26,8 +25,6 @@ Project: Sift implementation in Python + OpenCL https://github.com/silx-kit/silx """ -from __future__ import division - __authors__ = ["Jérôme Kieffer", "Pierre Paleo"] __contact__ = "jerome.kieffer@esrf.eu" __license__ = "MIT" diff --git a/src/silx/resources/__init__.py b/src/silx/resources/__init__.py index 5346f48..b53f15b 100644 --- a/src/silx/resources/__init__.py +++ b/src/silx/resources/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/resources/gui/icons/add-shape-rotated-rectangle.png b/src/silx/resources/gui/icons/add-shape-rotated-rectangle.png Binary files differnew file mode 100644 index 0000000..d07efe5 --- /dev/null +++ b/src/silx/resources/gui/icons/add-shape-rotated-rectangle.png diff --git a/src/silx/resources/gui/icons/add-shape-rotated-rectangle.svg b/src/silx/resources/gui/icons/add-shape-rotated-rectangle.svg new file mode 100644 index 0000000..5f308e0 --- /dev/null +++ b/src/silx/resources/gui/icons/add-shape-rotated-rectangle.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><rect transform="rotate(37.949)" x="11.714" y="-3.8806" width="21.14" height="12.56" fill="none" stroke="#f7941e" stroke-miterlimit="10" stroke-width="3"/><g transform="translate(.25293 13.263)" fill="#00a651" stroke="#00a651" stroke-miterlimit="10"><rect x="24.483" y="7.225" width="1.239" height="8.379"/><rect x="20.913" y="10.796" width="8.38" height="1.237"/></g></svg> diff --git a/src/silx/resources/gui/icons/scale-auto.png b/src/silx/resources/gui/icons/scale-auto.png Binary files differnew file mode 100644 index 0000000..35c68b4 --- /dev/null +++ b/src/silx/resources/gui/icons/scale-auto.png diff --git a/src/silx/resources/gui/icons/scale-auto.svg b/src/silx/resources/gui/icons/scale-auto.svg new file mode 100644 index 0000000..573e16e --- /dev/null +++ b/src/silx/resources/gui/icons/scale-auto.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg id="svg8" version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata id="metadata14"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><g id="text841" transform="matrix(.60674 0 0 1.2718 .00017519 3.6533)" stroke-width=".42868" aria-label="AUTO"><path id="path1407" d="m10.2 13.672h-5.0403l-0.7954 2.2774h-3.2402l4.6301-12.5h3.843l4.6301 12.5h-3.2402zm-4.2366-2.3192h3.4244l-1.708-4.9734z"/><path id="path1409" d="m14.846 3.4493h3.2235v7.4935q0 1.5489 0.50236 2.2188 0.51073 0.66144 1.6578 0.66144 1.1554 0 1.6578-0.66144 0.51073-0.66981 0.51073-2.2188v-7.4935h3.2235v7.4935q0 2.6541-1.3313 3.9519t-4.0607 1.2978q-2.7211 0-4.0524-1.2978t-1.3313-3.9519z"/><path id="path1411" d="m26.793 3.4493h11.521v2.4364h-4.1445v10.064h-3.2235v-10.064h-4.1528z"/><path id="path1413" d="m45.186 5.5592q-1.4736 0-2.2857 1.0884-0.81215 1.0884-0.81215 3.0644 0 1.9676 0.81215 3.056t2.2857 1.0884q1.482 0 2.2941-1.0884 0.81215-1.0884 0.81215-3.056 0-1.9759-0.81215-3.0644-0.81215-1.0884-2.2941-1.0884zm0-2.336q3.0142 0 4.7222 1.7248t1.708 4.764q0 3.0309-1.708 4.7557t-4.7222 1.7248q-3.0058 0-4.7222-1.7248-1.708-1.7248-1.708-4.7557 0-3.0393 1.708-4.764 1.7164-1.7248 4.7222-1.7248z"/></g></svg> diff --git a/src/silx/resources/gui/icons/scale-fixed.png b/src/silx/resources/gui/icons/scale-fixed.png Binary files differnew file mode 100644 index 0000000..9e765c2 --- /dev/null +++ b/src/silx/resources/gui/icons/scale-fixed.png diff --git a/src/silx/resources/gui/icons/scale-fixed.svg b/src/silx/resources/gui/icons/scale-fixed.svg new file mode 100644 index 0000000..93fd99a --- /dev/null +++ b/src/silx/resources/gui/icons/scale-fixed.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg id="svg8" version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata id="metadata14"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><g id="text841-0" transform="matrix(.60674 0 0 1.2718 .00017519 3.6533)" fill="#4d4d4d" stroke-width=".42868" aria-label="AUTO"><path id="path1407" d="m10.2 13.672h-5.0403l-0.7954 2.2774h-3.2402l4.6301-12.5h3.843l4.6301 12.5h-3.2402zm-4.2366-2.3192h3.4244l-1.708-4.9734z"/><path id="path1409" d="m14.846 3.4493h3.2235v7.4935q0 1.5489 0.50236 2.2188 0.51073 0.66144 1.6578 0.66144 1.1554 0 1.6578-0.66144 0.51073-0.66981 0.51073-2.2188v-7.4935h3.2235v7.4935q0 2.6541-1.3313 3.9519t-4.0607 1.2978q-2.7211 0-4.0524-1.2978t-1.3313-3.9519z"/><path id="path1411" d="m26.793 3.4493h11.521v2.4364h-4.1445v10.064h-3.2235v-10.064h-4.1528z"/><path id="path1413" d="m45.186 5.5592q-1.4736 0-2.2857 1.0884-0.81215 1.0884-0.81215 3.0644 0 1.9676 0.81215 3.056t2.2857 1.0884q1.482 0 2.2941-1.0884 0.81215-1.0884 0.81215-3.056 0-1.9759-0.81215-3.0644-0.81215-1.0884-2.2941-1.0884zm0-2.336q3.0142 0 4.7222 1.7248t1.708 4.764q0 3.0309-1.708 4.7557t-4.7222 1.7248q-3.0058 0-4.7222-1.7248-1.708-1.7248-1.708-4.7557 0-3.0393 1.708-4.764 1.7164-1.7248 4.7222-1.7248z"/></g><path id="rect846" transform="rotate(15.647)" d="m3.5335 9.776h32.378v2.6316h-32.378z" fill="#4d4d4d"/></svg> diff --git a/src/silx/setup.py b/src/silx/setup.py deleted file mode 100644 index 5e2bd0d..0000000 --- a/src/silx/setup.py +++ /dev/null @@ -1,54 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2015-2019 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "26/07/2018" - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('silx', parent_package, top_path) - config.add_subpackage('gui') - config.add_subpackage('io') - config.add_subpackage('math') - config.add_subpackage('image') - config.add_subpackage('opencl') - config.add_subpackage('resources') - config.add_subpackage('sx') - config.add_subpackage('test') - config.add_subpackage('third_party') - config.add_subpackage('utils') - config.add_subpackage('app') - config.add_subpackage("examples", "../../examples") - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/sx/__init__.py b/src/silx/sx/__init__.py index 97a3460..8922989 100644 --- a/src/silx/sx/__init__.py +++ b/src/silx/sx/__init__.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2019 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -88,7 +87,7 @@ def enable_gui(): global qt, _qapp - if _IS_NOTEBOOK: + if _get_ipython is not None and _get_ipython() is not None: _get_ipython().enable_pylab(gui='qt', import_all=False) from silx.gui import qt diff --git a/src/silx/sx/_plot.py b/src/silx/sx/_plot.py index b44c042..155adba 100644 --- a/src/silx/sx/_plot.py +++ b/src/silx/sx/_plot.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/sx/_plot3d.py b/src/silx/sx/_plot3d.py index 444d9e0..c6833ac 100644 --- a/src/silx/sx/_plot3d.py +++ b/src/silx/sx/_plot3d.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018-2019 European Synchrotron Radiation Facility diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index d9d3e42..31a892a 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2015-2021 European Synchrotron Radiation Facility +# Copyright (c) 2015-2022 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 @@ -49,5 +48,5 @@ def run_tests(module: str='silx', verbosity: int=0, args=()): str(verbosity), '-o python_files=["test/test*.py","test/Test*.py"]', '-o python_classes=["Test"]', - '-o python_functions=["Test"]', + '-o python_functions=["test"]', ] + list(args)) diff --git a/src/silx/test/test_resources.py b/src/silx/test/test_resources.py index 4030271..3344da0 100644 --- a/src/silx/test/test_resources.py +++ b/src/silx/test/test_resources.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/test/test_sx.py b/src/silx/test/test_sx.py index 9836285..1107964 100644 --- a/src/silx/test/test_sx.py +++ b/src/silx/test/test_sx.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility diff --git a/src/silx/test/test_version.py b/src/silx/test/test_version.py index 80084f9..c138d84 100644 --- a/src/silx/test/test_version.py +++ b/src/silx/test/test_version.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2016 European Synchrotron Radiation Facility diff --git a/src/silx/test/utils.py b/src/silx/test/utils.py index 0c2d5bf..5178e4b 100644 --- a/src/silx/test/utils.py +++ b/src/silx/test/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/third_party/__init__.py b/src/silx/third_party/__init__.py index 156563c..529ae3f 100644 --- a/src/silx/third_party/__init__.py +++ b/src/silx/third_party/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2015-2018 European Synchrotron Radiation Facility diff --git a/src/silx/third_party/scipy_spatial.py b/src/silx/third_party/scipy_spatial.py index 9885154..13069b3 100644 --- a/src/silx/third_party/scipy_spatial.py +++ b/src/silx/third_party/scipy_spatial.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2017 European Synchrotron Radiation Facility @@ -35,8 +34,6 @@ It should be used like that: """ -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "07/11/2017" diff --git a/src/silx/third_party/setup.py b/src/silx/third_party/setup.py deleted file mode 100644 index 47686ea..0000000 --- a/src/silx/third_party/setup.py +++ /dev/null @@ -1,49 +0,0 @@ -# coding: ascii -# -# JK: Numpy.distutils which imports this does not handle utf-8 in version<1.12 -# -# /*########################################################################## -# -# Copyright (c) 2016-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. -# -# ###########################################################################*/ - -__authors__ = ["Valentin Valls"] -__license__ = "MIT" -__date__ = "23/04/2018" - -import os -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('third_party', parent_package, top_path) - # includes _local only if it is available - local_path = os.path.join(top_path, "src", "silx", "third_party", "_local") - if os.path.exists(local_path): - config.add_subpackage('_local') - config.add_subpackage('_local.scipy_spatial') - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) diff --git a/src/silx/utils/ExternalResources.py b/src/silx/utils/ExternalResources.py index b79d6ff..429314e 100644 --- a/src/silx/utils/ExternalResources.py +++ b/src/silx/utils/ExternalResources.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -27,7 +26,7 @@ __authors__ = ["Thomas Vincent", "J. Kieffer"] __license__ = "MIT" -__date__ = "08/03/2019" +__date__ = "21/12/2021" import os @@ -38,7 +37,8 @@ import tempfile import unittest import urllib.request import urllib.error - +import hashlib +from collections import OrderedDict logger = logging.getLogger(__name__) @@ -68,10 +68,10 @@ class ExternalResources(object): self.project = project self._initialized = False self.sem = threading.Semaphore() - + self.hash = hashlib.sha256 self.env_key = env_key or (self.project.upper() + "_TESTDATA") self.url_base = url_base - self.all_data = set() + self.all_data = {} self.timeout = timeout self._data_home = None @@ -104,6 +104,21 @@ class ExternalResources(object): self._data_home = data_home return data_home + def get_hash(self, filename=None, data=None): + "Calculate and return the hash of a file or a bunch of data" + if data is None and filename is None: + return + h = self.hash() + if filename is not None: + fullfilename = os.path.join(self.data_home, filename) + if os.path.exists(fullfilename): + with open(fullfilename, "rb") as fd: + data = fd.read() + else: + raise RuntimeError(f"Filename {fullfilename} does not exist !") + h.update(data) + return h.hexdigest() + def _initialize_data(self): """Initialize for downloading test data""" if not self._initialized: @@ -112,7 +127,13 @@ class ExternalResources(object): self.testdata = os.path.join(self.data_home, "all_testdata.json") if os.path.exists(self.testdata): with open(self.testdata) as f: - self.all_data = set(json.load(f)) + jdata = json.load(f) + if isinstance(jdata, dict): + self.all_data = jdata + else: + #recalculate the hash only if the data was stored as a list + self.all_data = {k: self.get_hash(k) for k in jdata} + self.save_json() self._initialized = True def clean_up(self): @@ -160,7 +181,7 @@ class ExternalResources(object): os.makedirs(os.path.dirname(fullfilename)) try: - with open(fullfilename, "wb") as outfile: + with open(fullfilename, mode="wb") as outfile: outfile.write(data) except IOError: raise IOError("unable to write downloaded \ @@ -173,19 +194,32 @@ class ExternalResources(object): This even works under windows ! Otherwise please try to download the images manually from %s/%s""" % (filename, self.url_base, filename)) + else: + self.all_data[filename] = self.get_hash(data=data) + self.save_json() - if filename not in self.all_data: - self.all_data.add(filename) - image_list = list(self.all_data) - image_list.sort() - try: - with open(self.testdata, "w") as fp: - json.dump(image_list, fp, indent=4) - except IOError: - logger.debug("Unable to save JSON list") - + else: + h = self.hash() + with open(fullfilename, mode="rb") as fd: + h.update(fd.read()) + if h.hexdigest() != self.all_data[filename]: + logger.warning(f"Detected corruped file {fullfilename}") + self.all_data.pop(filename) + os.unlink(fullfilename) + return self.getfile(filename) + return fullfilename + def save_json(self): + image_list = list(self.all_data.keys()) + image_list.sort() + dico = OrderedDict([(i, self.all_data[i]) for i in image_list]) + try: + with open(self.testdata, "w") as fp: + json.dump(dico, fp, indent=4) + except IOError: + logger.info("Unable to save JSON dict") + def getdir(self, dirname): """Downloads the requested tarball from the server https://www.silx.org/pub/silx/ @@ -227,14 +261,8 @@ class ExternalResources(object): if not self._initialized: self._initialize_data() if filename not in self.all_data: - self.all_data.add(filename) - image_list = list(self.all_data) - image_list.sort() - try: - with open(self.testdata, "w") as fp: - json.dump(image_list, fp, indent=4) - except IOError: - logger.debug("Unable to save JSON list") + self.all_data[filename] = self.get_hash(filename) + seld.save_json() baseimage = os.path.basename(filename) logger.info("UtilsTest.getimage('%s')" % baseimage) @@ -313,7 +341,7 @@ class ExternalResources(object): if not self._initialized: self._initialize_data() if not imgs: - imgs = self.all_data + imgs = self.all_data.keys() res = [] for fn in imgs: logger.info("Downloading from silx.org: %s", fn) diff --git a/src/silx/utils/__init__.py b/src/silx/utils/__init__.py index f803a5f..6505e84 100644 --- a/src/silx/utils/__init__.py +++ b/src/silx/utils/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2018 European Synchrotron Radiation Facility diff --git a/src/silx/utils/_have_openmp.pxd b/src/silx/utils/_have_openmp.pxd index 89a385c..7302956 100644 --- a/src/silx/utils/_have_openmp.pxd +++ b/src/silx/utils/_have_openmp.pxd @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/utils/array_like.py b/src/silx/utils/array_like.py index 0cf4857..d9c7b73 100644 --- a/src/silx/utils/array_like.py +++ b/src/silx/utils/array_like.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -47,8 +46,6 @@ Functions: """ -from __future__ import absolute_import, print_function, division - import sys import numpy diff --git a/src/silx/utils/debug.py b/src/silx/utils/debug.py index 3d50fc9..ec361ac 100644 --- a/src/silx/utils/debug.py +++ b/src/silx/utils/debug.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/deprecation.py b/src/silx/utils/deprecation.py index 7b19ee5..81d7ed1 100644 --- a/src/silx/utils/deprecation.py +++ b/src/silx/utils/deprecation.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Bunch of useful decorators""" -from __future__ import absolute_import, print_function, division - __authors__ = ["Jerome Kieffer", "H. Payno", "P. Knobel"] __license__ = "MIT" __date__ = "26/02/2018" diff --git a/src/silx/utils/enum.py b/src/silx/utils/enum.py index fece575..176d429 100644 --- a/src/silx/utils/enum.py +++ b/src/silx/utils/enum.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """An :class:`.Enum` class with additional features.""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "29/04/2019" diff --git a/src/silx/utils/exceptions.py b/src/silx/utils/exceptions.py index addba89..d7e5533 100644 --- a/src/silx/utils/exceptions.py +++ b/src/silx/utils/exceptions.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/utils/files.py b/src/silx/utils/files.py index 1982c0d..ab8d417 100644 --- a/src/silx/utils/files.py +++ b/src/silx/utils/files.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility diff --git a/src/silx/utils/html.py b/src/silx/utils/html.py index 9b39b95..654c780 100644 --- a/src/silx/utils/html.py +++ b/src/silx/utils/html.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/launcher.py b/src/silx/utils/launcher.py index c46256a..20752b3 100644 --- a/src/silx/utils/launcher.py +++ b/src/silx/utils/launcher.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2004-2017 European Synchrotron Radiation Facility diff --git a/src/silx/utils/number.py b/src/silx/utils/number.py index f852a39..72106e7 100755 --- a/src/silx/utils/number.py +++ b/src/silx/utils/number.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility diff --git a/src/silx/utils/property.py b/src/silx/utils/property.py index 10d5d98..029f28e 100644 --- a/src/silx/utils/property.py +++ b/src/silx/utils/property.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Bunch of useful decorators""" -from __future__ import absolute_import, print_function, division - __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "22/02/2018" diff --git a/src/silx/utils/proxy.py b/src/silx/utils/proxy.py index d8821c2..7801b4b 100644 --- a/src/silx/utils/proxy.py +++ b/src/silx/utils/proxy.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Module containing proxy objects""" -from __future__ import absolute_import, print_function, division - __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "02/10/2017" diff --git a/src/silx/utils/retry.py b/src/silx/utils/retry.py index adc43bc..804bcb6 100644 --- a/src/silx/utils/retry.py +++ b/src/silx/utils/retry.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # @@ -32,6 +31,7 @@ __date__ = "05/02/2020" import time +import inspect from functools import wraps from contextlib import contextmanager import multiprocessing @@ -58,13 +58,13 @@ def _default_retry_on_error(e): @contextmanager -def _handle_exception(options): +def _handle_exception(retry_state): try: yield except BaseException as e: - retry_on_error = options.get("retry_on_error") + retry_on_error = retry_state.get("retry_on_error") if retry_on_error is not None and retry_on_error(e): - options["exception"] = e + retry_state["exception"] = e else: raise @@ -79,15 +79,19 @@ def _retry_loop(retry_timeout=None, retry_period=None, retry_on_error=None): eligible for retry """ has_timeout = retry_timeout is not None - options = {"exception": None, "retry_on_error": retry_on_error} if has_timeout: t0 = time.time() + else: + t0 = None + retry_state = {"t0": t0, "exception": None, "retry_on_error": retry_on_error} while True: - yield options + yield retry_state if retry_period is not None: time.sleep(retry_period) if has_timeout and (time.time() - t0) > retry_timeout: - raise RetryTimeoutError from options.get("exception") + err_msg = "%s seconds" % retry_timeout + cause = retry_state.get("exception") + raise RetryTimeoutError(err_msg) from cause def retry( @@ -99,6 +103,9 @@ def retry( The decorator arguments can be overriden by using them when calling the decorated method. + Generator functions are required to have a `start_index` argument which allows + the method to start iterating from the last failure when called on retry. + :param num retry_timeout: :param num retry_period: sleep before retry :param callable or None retry_on_error: checks whether an exception is @@ -109,18 +116,53 @@ def retry( retry_period = RETRY_PERIOD def decorator(method): - @wraps(method) - def wrapper(*args, **kw): - _retry_timeout = kw.pop("retry_timeout", retry_timeout) - _retry_period = kw.pop("retry_period", retry_period) - _retry_on_error = kw.pop("retry_on_error", retry_on_error) - for options in _retry_loop( - retry_timeout=_retry_timeout, - retry_period=_retry_period, - retry_on_error=_retry_on_error, - ): - with _handle_exception(options): - return method(*args, **kw) + if inspect.isgeneratorfunction(method): + if "start_index" not in inspect.signature(method).parameters: + raise TypeError( + "The generator function '%s' needs a `start_index` named argument because it is wrapped with the `retry` decorator." + % method.__name__ + ) + + @wraps(method) + def wrapper(*args, **kw): + _retry_timeout = kw.pop("retry_timeout", retry_timeout) + _retry_period = kw.pop("retry_period", retry_period) + _retry_on_error = kw.pop("retry_on_error", retry_on_error) + start_index = kw.pop("start_index", 0) + if start_index is None: + start_index = 0 + for retry_state in _retry_loop( + retry_timeout=_retry_timeout, + retry_period=_retry_period, + retry_on_error=_retry_on_error, + ): + with _handle_exception(retry_state): + oretry_on_error = retry_state["retry_on_error"] + for result in method(*args, start_index=start_index, **kw): + start_index += 1 + retry_state["retry_on_error"] = None + # any exception here will NOT cause a retry + yield result + # restart the retry loop + if retry_state["t0"] is not None: + retry_state["t0"] = time.time() + retry_state["retry_on_error"] = oretry_on_error + return + + else: + + @wraps(method) + def wrapper(*args, **kw): + _retry_timeout = kw.pop("retry_timeout", retry_timeout) + _retry_period = kw.pop("retry_period", retry_period) + _retry_on_error = kw.pop("retry_on_error", retry_on_error) + for retry_state in _retry_loop( + retry_timeout=_retry_timeout, + retry_period=_retry_period, + retry_on_error=_retry_on_error, + ): + with _handle_exception(retry_state): + return method(*args, **kw) return wrapper @@ -151,18 +193,18 @@ def retry_contextmanager( _retry_timeout = kw.pop("retry_timeout", retry_timeout) _retry_period = kw.pop("retry_period", retry_period) _retry_on_error = kw.pop("retry_on_error", retry_on_error) - for options in _retry_loop( + for retry_state in _retry_loop( retry_timeout=_retry_timeout, retry_period=_retry_period, retry_on_error=_retry_on_error, ): - with _handle_exception(options): - gen = method(*args, **kw) - result = next(gen) - options["retry_on_error"] = None + with _handle_exception(retry_state): + ctx = method(*args, **kw) + result = next(ctx) + retry_state["retry_on_error"] = None yield result try: - next(gen) + next(ctx) except StopIteration: return else: @@ -238,10 +280,10 @@ def retry_in_subprocess( p, queue = start_subprocess() try: - for options in _retry_loop( + for retry_state in _retry_loop( retry_timeout=_retry_timeout, retry_on_error=_retry_on_error ): - with _handle_exception(options): + with _handle_exception(retry_state): if not p.is_alive(): p, queue = start_subprocess() try: diff --git a/src/silx/utils/setup.py b/src/silx/utils/setup.py deleted file mode 100644 index 1f3e09a..0000000 --- a/src/silx/utils/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "24/08/2016" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('utils', parent_package, top_path) - config.add_subpackage('test') - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/src/silx/utils/test/__init__.py b/src/silx/utils/test/__init__.py index 14fd940..88135c3 100755 --- a/src/silx/utils/test/__init__.py +++ b/src/silx/utils/test/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_array_like.py b/src/silx/utils/test/test_array_like.py index a0b4b7b..309b9ff 100644 --- a/src/silx/utils/test/test_array_like.py +++ b/src/silx/utils/test/test_array_like.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2017 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_debug.py b/src/silx/utils/test/test_debug.py index 09f4b01..6b7b5d6 100644 --- a/src/silx/utils/test/test_debug.py +++ b/src/silx/utils/test/test_debug.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_deprecation.py b/src/silx/utils/test/test_deprecation.py index d52cb26..798221a 100644 --- a/src/silx/utils/test/test_deprecation.py +++ b/src/silx/utils/test/test_deprecation.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_enum.py b/src/silx/utils/test/test_enum.py index 808304a..df6b266 100644 --- a/src/silx/utils/test/test_enum.py +++ b/src/silx/utils/test/test_enum.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2019 European Synchrotron Radiation Facility @@ -24,8 +23,6 @@ # ###########################################################################*/ """Tests of Enum class with extra class methods""" -from __future__ import absolute_import - __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "29/04/2019" diff --git a/src/silx/utils/test/test_external_resources.py b/src/silx/utils/test/test_external_resources.py index 1fedda3..6279460 100644 --- a/src/silx/utils/test/test_external_resources.py +++ b/src/silx/utils/test/test_external_resources.py @@ -1,7 +1,6 @@ -# coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2022 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 @@ -59,7 +58,7 @@ class TestExternalResources(unittest.TestCase): raise unittest.SkipTest("Network or silx website not available") def setUp(self): - self.resources = ExternalResources("toto", "http://www.silx.org/pub/silx/") + self.resources = ExternalResources("toto%d" % os.getpid(), "http://www.silx.org/pub/silx/") def tearDown(self): if self.resources.data_home: diff --git a/src/silx/utils/test/test_launcher.py b/src/silx/utils/test/test_launcher.py index 9e9024c..3261df5 100644 --- a/src/silx/utils/test/test_launcher.py +++ b/src/silx/utils/test/test_launcher.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_launcher_command.py b/src/silx/utils/test/test_launcher_command.py index ccf4601..ff9f336 100644 --- a/src/silx/utils/test/test_launcher_command.py +++ b/src/silx/utils/test/test_launcher_command.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_number.py b/src/silx/utils/test/test_number.py index 3eb8e91..8c6d1a2 100644 --- a/src/silx/utils/test/test_number.py +++ b/src/silx/utils/test/test_number.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2021 European Synchrotron Radiation Facility # diff --git a/src/silx/utils/test/test_proxy.py b/src/silx/utils/test/test_proxy.py index e165267..7af4d1f 100644 --- a/src/silx/utils/test/test_proxy.py +++ b/src/silx/utils/test/test_proxy.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_retry.py b/src/silx/utils/test/test_retry.py index 39bfdcf..7e06e65 100644 --- a/src/silx/utils/test/test_retry.py +++ b/src/silx/utils/test/test_retry.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # @@ -167,3 +166,47 @@ class TestRetry(unittest.TestCase): f.write("0") with self.assertRaises(retry.RetryTimeoutError): _wsubmain(self.ctr_file, **kw) + + def test_retry_generator(self): + ncausefailure = 3 + faildelay = 0.1 + sufficient_timeout = ncausefailure * (faildelay + 10) + insufficient_timeout = ncausefailure * faildelay * 0.5 + + @retry.retry() + def method(check, kwcheck=None, start_index=0): + if start_index <= 0: + yield 0 + assert check + assert kwcheck + nonlocal failcounter + if failcounter < ncausefailure: + time.sleep(faildelay) + failcounter += 1 + if start_index <= 1: + yield 1 + raise retry.RetryError + else: + if start_index <= 1: + yield 1 + if start_index <= 2: + yield 2 + + failcounter = 0 + kw = {"kwcheck": True, "retry_timeout": sufficient_timeout} + self.assertEqual(list(method(True, **kw)), [0, 1, 2]) + + failcounter = 0 + kw = { + "kwcheck": True, + "retry_timeout": insufficient_timeout, + } + with self.assertRaises(retry.RetryTimeoutError): + list(method(True, **kw)) + + def test_retry_wrong_generator(self): + with self.assertRaises(TypeError): + + @retry.retry() + def method(): + yield from range(3) diff --git a/src/silx/utils/test/test_testutils.py b/src/silx/utils/test/test_testutils.py index 4f07c4e..a0624b1 100755 --- a/src/silx/utils/test/test_testutils.py +++ b/src/silx/utils/test/test_testutils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/test/test_weakref.py b/src/silx/utils/test/test_weakref.py index 06f3adf..4e3bf21 100644 --- a/src/silx/utils/test/test_weakref.py +++ b/src/silx/utils/test/test_weakref.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2019 European Synchrotron Radiation Facility diff --git a/src/silx/utils/testutils.py b/src/silx/utils/testutils.py index 4177e33..b331829 100755 --- a/src/silx/utils/testutils.py +++ b/src/silx/utils/testutils.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2021 European Synchrotron Radiation Facility diff --git a/src/silx/utils/weakref.py b/src/silx/utils/weakref.py index 06646e8..62a9232 100644 --- a/src/silx/utils/weakref.py +++ b/src/silx/utils/weakref.py @@ -1,4 +1,3 @@ -# coding: utf-8 # /*########################################################################## # # Copyright (c) 2016-2018 European Synchrotron Radiation Facility @@ -25,7 +24,6 @@ """Weakref utils for compatibility between Python 2 and Python 3 or for extended features. """ -from __future__ import absolute_import __authors__ = ["V. Valls"] __license__ = "MIT" |