summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst131
-rw-r--r--LICENSE11
-rw-r--r--PKG-INFO4
-rw-r--r--doc/source/Tutorials/io.rst28
-rw-r--r--doc/source/conf.py24
-rw-r--r--doc/source/ext/snapshotqt_directive.py2
-rw-r--r--doc/source/ext/sphinxext-archive.py1
-rw-r--r--doc/source/install.rst60
-rw-r--r--doc/source/license.rst12
-rw-r--r--doc/source/modules/gui/data/img/ArrayTableWidget.pngbin41977 -> 48408 bytes
-rw-r--r--doc/source/modules/gui/data/img/DataViewer.pngbin37500 -> 42376 bytes
-rw-r--r--doc/source/modules/gui/icons.rst9
-rw-r--r--doc/source/modules/gui/plot/getting_started.rst2
-rw-r--r--doc/source/modules/gui/plot/img/BasicGridStatsWidget.pngbin20668 -> 13381 bytes
-rw-r--r--doc/source/modules/gui/plot/img/BasicStatsWidget.pngbin8699 -> 9452 bytes
-rw-r--r--doc/source/modules/gui/plot/img/LimitsToolBar.pngbin21679 -> 21920 bytes
-rw-r--r--doc/source/modules/gui/plot/img/ROIStatsWidget.pngbin9659 -> 7176 bytes
-rw-r--r--doc/source/modules/gui/plot/img/logColorbar.pngbin12390 -> 7855 bytes
-rw-r--r--doc/source/modules/gui/plot3d/img/SceneWidget.pngbin73947 -> 74504 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/FrameBrowser.pngbin4538 -> 3379 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/HorizontalSliderWithBrowser.pngbin2884 -> 2987 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/PeriodicCombo.pngbin3124 -> 2912 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/PeriodicList.pngbin38615 -> 27993 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/PeriodicTable.pngbin61338 -> 35882 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/RangeSlider.pngbin1028 -> 1024 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/TableWidget.pngbin3624 -> 3824 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/ThreadPoolPushButton.pngbin2566 -> 2383 bytes
-rw-r--r--doc/source/modules/gui/widgets/img/WaitingPushButton.pngbin1187 -> 1148 bytes
-rw-r--r--doc/source/modules/io/commonh5.rst28
-rw-r--r--doc/source/modules/io/fioh5.rst8
-rw-r--r--doc/source/modules/io/index.rst3
-rw-r--r--doc/source/modules/io/spech5.rst8
-rw-r--r--doc/source/overview.rst7
-rw-r--r--doc/source/sample_code/img/plotUpdateImageFromGevent.pngbin0 -> 50938 bytes
-rw-r--r--doc/source/sample_code/index.rst4
-rw-r--r--doc/source/virtualenv.rst2
-rw-r--r--examples/__init__.py1
-rw-r--r--examples/colormapDialog.py1
-rw-r--r--examples/compareImages.py1
-rw-r--r--examples/compositeline.py2
-rw-r--r--examples/customDataView.py1
-rw-r--r--examples/customHdf5TreeModel.py1
-rw-r--r--examples/customSilxView.py1
-rw-r--r--examples/dropZones.py3
-rw-r--r--examples/exampleBaseline.py1
-rwxr-xr-xexamples/fftPlotAction.py1
-rw-r--r--examples/fileDialog.py3
-rw-r--r--examples/findContours.py1
-rwxr-xr-xexamples/hdf5widget.py1
-rw-r--r--examples/icons.py1
-rw-r--r--examples/imageStack.py1
-rwxr-xr-xexamples/imageview.py2
-rw-r--r--examples/periodicTable.py1
-rw-r--r--examples/plot3dContextMenu.py3
-rw-r--r--examples/plot3dSceneWindow.py3
-rw-r--r--examples/plot3dUpdateScatterFromThread.py1
-rw-r--r--examples/plotClearAction.py1
-rw-r--r--examples/plotContextMenu.py1
-rw-r--r--examples/plotCurveLegendWidget.py1
-rw-r--r--examples/plotInteractiveImageROI.py1
-rwxr-xr-xexamples/plotItemsSelector.py1
-rw-r--r--examples/plotLimits.py1
-rw-r--r--examples/plotProfile.py1
-rw-r--r--examples/plotROIStats.py1
-rw-r--r--examples/plotStats.py1
-rw-r--r--examples/plotUpdateCurveFromThread.py1
-rw-r--r--examples/plotUpdateImageFromGevent.py112
-rw-r--r--examples/plotUpdateImageFromThread.py1
-rw-r--r--examples/plotWidget.py1
-rwxr-xr-xexamples/printPreview.py1
-rw-r--r--examples/scatterMask.py1
-rwxr-xr-xexamples/scatterview.py2
-rwxr-xr-xexamples/shiftPlotAction.py1
-rwxr-xr-xexamples/simplewidget.py1
-rw-r--r--examples/stackView.py1
-rw-r--r--examples/syncPlotLocation.py1
-rw-r--r--examples/syncaxis.py1
-rw-r--r--examples/viewer3DVolume.py3
-rw-r--r--examples/writetoh5.py1
-rwxr-xr-xpackage/debian10/rules1
-rwxr-xr-xpackage/debian11/rules1
-rw-r--r--package/windows/bootstrap-silx-view.py1
-rw-r--r--package/windows/bootstrap.py1
-rw-r--r--package/windows/create-installer.iss.template2
-rw-r--r--package/windows/pyinstaller.spec29
-rw-r--r--pyproject.toml2
-rw-r--r--qtdesigner_plugins/plot1dplugin.py1
-rw-r--r--qtdesigner_plugins/plot2dplugin.py1
-rw-r--r--qtdesigner_plugins/plotwidgetplugin.py1
-rw-r--r--qtdesigner_plugins/plotwindowplugin.py1
-rw-r--r--requirements-dev.txt4
-rw-r--r--requirements.txt6
-rwxr-xr-xrun_tests.py21
-rw-r--r--setup.py695
-rw-r--r--src/silx.egg-info/PKG-INFO (renamed from silx.egg-info/PKG-INFO)4
-rw-r--r--src/silx.egg-info/SOURCES.txt (renamed from silx.egg-info/SOURCES.txt)51
-rw-r--r--src/silx.egg-info/dependency_links.txt (renamed from silx.egg-info/dependency_links.txt)0
-rw-r--r--src/silx.egg-info/entry_points.txt (renamed from silx.egg-info/entry_points.txt)0
-rw-r--r--src/silx.egg-info/not-zip-safe (renamed from silx.egg-info/not-zip-safe)0
-rw-r--r--src/silx.egg-info/requires.txt (renamed from silx.egg-info/requires.txt)0
-rw-r--r--src/silx.egg-info/top_level.txt (renamed from silx.egg-info/top_level.txt)0
-rw-r--r--src/silx/__init__.py3
-rw-r--r--src/silx/__main__.py1
-rw-r--r--src/silx/_config.py1
-rw-r--r--src/silx/_version.py6
-rw-r--r--src/silx/app/__init__.py1
-rw-r--r--src/silx/app/convert.py1
-rw-r--r--src/silx/app/setup.py41
-rw-r--r--src/silx/app/test/__init__.py1
-rw-r--r--src/silx/app/test/test_convert.py1
-rw-r--r--src/silx/app/test_.py1
-rw-r--r--src/silx/app/view/About.py7
-rw-r--r--src/silx/app/view/ApplicationContext.py1
-rw-r--r--src/silx/app/view/CustomNxdataWidget.py1
-rw-r--r--src/silx/app/view/DataPanel.py1
-rw-r--r--src/silx/app/view/Viewer.py26
-rw-r--r--src/silx/app/view/__init__.py1
-rw-r--r--src/silx/app/view/main.py46
-rw-r--r--src/silx/app/view/setup.py40
-rw-r--r--src/silx/app/view/test/__init__.py1
-rw-r--r--src/silx/app/view/test/test_launcher.py1
-rw-r--r--src/silx/app/view/test/test_view.py1
-rw-r--r--src/silx/app/view/utils.py1
-rw-r--r--src/silx/conftest.py5
-rw-r--r--src/silx/gui/__init__.py1
-rw-r--r--src/silx/gui/_glutils/Context.py1
-rw-r--r--src/silx/gui/_glutils/FramebufferTexture.py1
-rw-r--r--src/silx/gui/_glutils/OpenGLWidget.py7
-rw-r--r--src/silx/gui/_glutils/Program.py1
-rw-r--r--src/silx/gui/_glutils/Texture.py1
-rw-r--r--src/silx/gui/_glutils/VertexBuffer.py1
-rw-r--r--src/silx/gui/_glutils/__init__.py1
-rw-r--r--src/silx/gui/_glutils/font.py73
-rw-r--r--src/silx/gui/_glutils/gl.py15
-rw-r--r--src/silx/gui/_glutils/utils.py1
-rwxr-xr-xsrc/silx/gui/colors.py3
-rw-r--r--src/silx/gui/console.py10
-rw-r--r--src/silx/gui/data/ArrayTableModel.py10
-rw-r--r--src/silx/gui/data/ArrayTableWidget.py2
-rw-r--r--src/silx/gui/data/DataViewer.py20
-rw-r--r--src/silx/gui/data/DataViewerFrame.py16
-rw-r--r--src/silx/gui/data/DataViewerSelector.py2
-rw-r--r--src/silx/gui/data/DataViews.py19
-rw-r--r--src/silx/gui/data/Hdf5TableView.py7
-rw-r--r--src/silx/gui/data/HexaTableView.py2
-rw-r--r--src/silx/gui/data/NXdataWidgets.py14
-rw-r--r--src/silx/gui/data/NumpyAxesSelector.py2
-rw-r--r--src/silx/gui/data/RecordTableView.py2
-rw-r--r--src/silx/gui/data/TextFormatter.py1
-rw-r--r--src/silx/gui/data/_VolumeWindow.py1
-rw-r--r--src/silx/gui/data/__init__.py1
-rw-r--r--src/silx/gui/data/setup.py41
-rw-r--r--src/silx/gui/data/test/__init__.py1
-rw-r--r--src/silx/gui/data/test/test_arraywidget.py1
-rw-r--r--src/silx/gui/data/test/test_dataviewer.py21
-rw-r--r--src/silx/gui/data/test/test_numpyaxesselector.py1
-rw-r--r--src/silx/gui/data/test/test_textformatter.py1
-rw-r--r--src/silx/gui/dialog/AbstractDataFileDialog.py22
-rw-r--r--src/silx/gui/dialog/ColormapDialog.py317
-rw-r--r--src/silx/gui/dialog/DataFileDialog.py1
-rw-r--r--src/silx/gui/dialog/DatasetDialog.py1
-rw-r--r--src/silx/gui/dialog/FileTypeComboBox.py1
-rw-r--r--src/silx/gui/dialog/GroupDialog.py1
-rw-r--r--src/silx/gui/dialog/ImageFileDialog.py1
-rw-r--r--src/silx/gui/dialog/SafeFileIconProvider.py1
-rw-r--r--src/silx/gui/dialog/SafeFileSystemModel.py1
-rw-r--r--src/silx/gui/dialog/__init__.py1
-rw-r--r--src/silx/gui/dialog/setup.py40
-rw-r--r--src/silx/gui/dialog/test/__init__.py1
-rw-r--r--src/silx/gui/dialog/test/test_colormapdialog.py22
-rw-r--r--src/silx/gui/dialog/test/test_datafiledialog.py13
-rw-r--r--src/silx/gui/dialog/test/test_imagefiledialog.py13
-rw-r--r--src/silx/gui/dialog/utils.py1
-rw-r--r--src/silx/gui/fit/BackgroundWidget.py1
-rw-r--r--src/silx/gui/fit/FitConfig.py1
-rw-r--r--src/silx/gui/fit/FitWidget.py1
-rw-r--r--src/silx/gui/fit/FitWidgets.py1
-rw-r--r--src/silx/gui/fit/Parameters.py1
-rw-r--r--src/silx/gui/fit/__init__.py1
-rw-r--r--src/silx/gui/fit/setup.py43
-rw-r--r--src/silx/gui/fit/test/__init__.py1
-rw-r--r--src/silx/gui/fit/test/testBackgroundWidget.py1
-rw-r--r--src/silx/gui/fit/test/testFitConfig.py1
-rw-r--r--src/silx/gui/fit/test/testFitWidget.py1
-rw-r--r--src/silx/gui/hdf5/Hdf5Formatter.py1
-rw-r--r--src/silx/gui/hdf5/Hdf5HeaderView.py1
-rwxr-xr-xsrc/silx/gui/hdf5/Hdf5Item.py21
-rw-r--r--src/silx/gui/hdf5/Hdf5LoadingItem.py12
-rw-r--r--src/silx/gui/hdf5/Hdf5Node.py17
-rw-r--r--src/silx/gui/hdf5/Hdf5TreeModel.py61
-rw-r--r--src/silx/gui/hdf5/Hdf5TreeView.py1
-rw-r--r--src/silx/gui/hdf5/NexusSortFilterProxyModel.py1
-rw-r--r--src/silx/gui/hdf5/__init__.py1
-rw-r--r--src/silx/gui/hdf5/_utils.py1
-rw-r--r--src/silx/gui/hdf5/setup.py41
-rw-r--r--src/silx/gui/hdf5/test/__init__.py1
-rwxr-xr-xsrc/silx/gui/hdf5/test/test_hdf5.py1
-rw-r--r--src/silx/gui/icons.py1
-rw-r--r--src/silx/gui/plot/AlphaSlider.py1
-rw-r--r--src/silx/gui/plot/ColorBar.py3
-rw-r--r--src/silx/gui/plot/Colormap.py1
-rw-r--r--src/silx/gui/plot/ColormapDialog.py3
-rw-r--r--src/silx/gui/plot/Colors.py3
-rw-r--r--src/silx/gui/plot/CompareImages.py1
-rw-r--r--src/silx/gui/plot/ComplexImageView.py3
-rw-r--r--src/silx/gui/plot/CurvesROIWidget.py15
-rw-r--r--src/silx/gui/plot/ImageStack.py1
-rw-r--r--src/silx/gui/plot/ImageView.py4
-rw-r--r--src/silx/gui/plot/Interaction.py1
-rw-r--r--src/silx/gui/plot/ItemsSelectionDialog.py1
-rwxr-xr-xsrc/silx/gui/plot/LegendSelector.py10
-rw-r--r--src/silx/gui/plot/LimitsHistory.py1
-rw-r--r--src/silx/gui/plot/MaskToolsWidget.py2
-rw-r--r--src/silx/gui/plot/PlotActions.py1
-rw-r--r--src/silx/gui/plot/PlotEvents.py1
-rw-r--r--src/silx/gui/plot/PlotInteraction.py1
-rw-r--r--src/silx/gui/plot/PlotToolButtons.py1
-rw-r--r--src/silx/gui/plot/PlotTools.py3
-rwxr-xr-xsrc/silx/gui/plot/PlotWidget.py68
-rw-r--r--src/silx/gui/plot/PlotWindow.py47
-rw-r--r--src/silx/gui/plot/PrintPreviewToolButton.py2
-rw-r--r--src/silx/gui/plot/Profile.py1
-rw-r--r--src/silx/gui/plot/ProfileMainWindow.py1
-rw-r--r--src/silx/gui/plot/ROIStatsWidget.py1
-rw-r--r--src/silx/gui/plot/ScatterMaskToolsWidget.py3
-rw-r--r--src/silx/gui/plot/ScatterView.py1
-rw-r--r--src/silx/gui/plot/StackView.py17
-rw-r--r--src/silx/gui/plot/StatsWidget.py1
-rw-r--r--src/silx/gui/plot/_BaseMaskToolsWidget.py17
-rw-r--r--src/silx/gui/plot/__init__.py1
-rw-r--r--src/silx/gui/plot/_utils/__init__.py1
-rw-r--r--src/silx/gui/plot/_utils/delaunay.py4
-rw-r--r--src/silx/gui/plot/_utils/dtime_ticklayout.py20
-rw-r--r--src/silx/gui/plot/_utils/panzoom.py1
-rw-r--r--src/silx/gui/plot/_utils/setup.py42
-rw-r--r--src/silx/gui/plot/_utils/test/__init__.py1
-rw-r--r--src/silx/gui/plot/_utils/test/test_dtime_ticklayout.py81
-rw-r--r--src/silx/gui/plot/_utils/test/test_ticklayout.py3
-rw-r--r--src/silx/gui/plot/_utils/ticklayout.py3
-rw-r--r--src/silx/gui/plot/actions/PlotAction.py4
-rw-r--r--src/silx/gui/plot/actions/PlotToolAction.py4
-rw-r--r--src/silx/gui/plot/actions/__init__.py1
-rwxr-xr-xsrc/silx/gui/plot/actions/control.py3
-rw-r--r--src/silx/gui/plot/actions/fit.py3
-rw-r--r--src/silx/gui/plot/actions/histogram.py5
-rw-r--r--src/silx/gui/plot/actions/io.py3
-rw-r--r--src/silx/gui/plot/actions/medfilt.py3
-rw-r--r--src/silx/gui/plot/actions/mode.py3
-rwxr-xr-xsrc/silx/gui/plot/backends/BackendBase.py7
-rwxr-xr-xsrc/silx/gui/plot/backends/BackendMatplotlib.py32
-rwxr-xr-xsrc/silx/gui/plot/backends/BackendOpenGL.py66
-rw-r--r--src/silx/gui/plot/backends/__init__.py1
-rw-r--r--src/silx/gui/plot/backends/glutils/GLPlotCurve.py96
-rw-r--r--src/silx/gui/plot/backends/glutils/GLPlotFrame.py66
-rw-r--r--src/silx/gui/plot/backends/glutils/GLPlotImage.py30
-rw-r--r--src/silx/gui/plot/backends/glutils/GLPlotItem.py11
-rw-r--r--src/silx/gui/plot/backends/glutils/GLPlotTriangles.py1
-rw-r--r--src/silx/gui/plot/backends/glutils/GLSupport.py1
-rw-r--r--src/silx/gui/plot/backends/glutils/GLText.py3
-rw-r--r--src/silx/gui/plot/backends/glutils/GLTexture.py1
-rw-r--r--src/silx/gui/plot/backends/glutils/PlotImageFile.py1
-rw-r--r--src/silx/gui/plot/backends/glutils/__init__.py1
-rw-r--r--src/silx/gui/plot/items/__init__.py5
-rw-r--r--src/silx/gui/plot/items/_arc_roi.py8
-rw-r--r--src/silx/gui/plot/items/_band_roi.py370
-rw-r--r--src/silx/gui/plot/items/_pick.py1
-rw-r--r--src/silx/gui/plot/items/_roi_base.py1
-rw-r--r--src/silx/gui/plot/items/axis.py27
-rw-r--r--src/silx/gui/plot/items/complex.py3
-rw-r--r--src/silx/gui/plot/items/core.py50
-rw-r--r--src/silx/gui/plot/items/curve.py1
-rw-r--r--src/silx/gui/plot/items/histogram.py1
-rw-r--r--src/silx/gui/plot/items/image.py1
-rw-r--r--src/silx/gui/plot/items/image_aggregated.py1
-rwxr-xr-xsrc/silx/gui/plot/items/marker.py1
-rw-r--r--src/silx/gui/plot/items/roi.py4
-rw-r--r--src/silx/gui/plot/items/scatter.py10
-rw-r--r--src/silx/gui/plot/items/shape.py225
-rw-r--r--src/silx/gui/plot/matplotlib/Colormap.py1
-rw-r--r--src/silx/gui/plot/matplotlib/__init__.py1
-rw-r--r--src/silx/gui/plot/setup.py54
-rw-r--r--src/silx/gui/plot/stats/__init__.py1
-rw-r--r--src/silx/gui/plot/stats/stats.py5
-rw-r--r--src/silx/gui/plot/stats/statshandler.py15
-rw-r--r--src/silx/gui/plot/test/__init__.py1
-rw-r--r--src/silx/gui/plot/test/testAlphaSlider.py1
-rw-r--r--src/silx/gui/plot/test/testColorBar.py1
-rw-r--r--src/silx/gui/plot/test/testCompareImages.py1
-rw-r--r--src/silx/gui/plot/test/testComplexImageView.py1
-rw-r--r--src/silx/gui/plot/test/testCurvesROIWidget.py14
-rw-r--r--src/silx/gui/plot/test/testImageStack.py1
-rw-r--r--src/silx/gui/plot/test/testImageView.py1
-rw-r--r--src/silx/gui/plot/test/testInteraction.py1
-rw-r--r--src/silx/gui/plot/test/testItem.py1
-rw-r--r--src/silx/gui/plot/test/testLegendSelector.py1
-rw-r--r--src/silx/gui/plot/test/testLimitConstraints.py1
-rw-r--r--src/silx/gui/plot/test/testMaskToolsWidget.py1
-rw-r--r--src/silx/gui/plot/test/testPixelIntensityHistoAction.py1
-rw-r--r--src/silx/gui/plot/test/testPlotActions.py1
-rw-r--r--src/silx/gui/plot/test/testPlotInteraction.py1
-rwxr-xr-xsrc/silx/gui/plot/test/testPlotWidget.py23
-rw-r--r--src/silx/gui/plot/test/testPlotWidgetNoBackend.py1
-rw-r--r--src/silx/gui/plot/test/testPlotWindow.py1
-rw-r--r--src/silx/gui/plot/test/testRoiStatsWidget.py1
-rw-r--r--src/silx/gui/plot/test/testSaveAction.py1
-rw-r--r--src/silx/gui/plot/test/testScatterMaskToolsWidget.py1
-rw-r--r--src/silx/gui/plot/test/testScatterView.py1
-rw-r--r--src/silx/gui/plot/test/testStackView.py1
-rw-r--r--src/silx/gui/plot/test/testStats.py1
-rw-r--r--src/silx/gui/plot/test/testUtilsAxis.py1
-rw-r--r--src/silx/gui/plot/test/utils.py1
-rw-r--r--src/silx/gui/plot/tools/CurveLegendsWidget.py3
-rw-r--r--src/silx/gui/plot/tools/LimitsToolBar.py4
-rw-r--r--src/silx/gui/plot/tools/PositionInfo.py48
-rw-r--r--src/silx/gui/plot/tools/RadarView.py1
-rw-r--r--src/silx/gui/plot/tools/__init__.py1
-rw-r--r--src/silx/gui/plot/tools/profile/ScatterProfileToolBar.py1
-rw-r--r--src/silx/gui/plot/tools/profile/__init__.py1
-rw-r--r--src/silx/gui/plot/tools/profile/core.py1
-rw-r--r--src/silx/gui/plot/tools/profile/editors.py6
-rw-r--r--src/silx/gui/plot/tools/profile/manager.py3
-rw-r--r--src/silx/gui/plot/tools/profile/rois.py20
-rw-r--r--src/silx/gui/plot/tools/profile/toolbar.py1
-rw-r--r--src/silx/gui/plot/tools/roi.py4
-rw-r--r--src/silx/gui/plot/tools/test/__init__.py1
-rw-r--r--src/silx/gui/plot/tools/test/testCurveLegendsWidget.py1
-rw-r--r--src/silx/gui/plot/tools/test/testProfile.py1
-rw-r--r--src/silx/gui/plot/tools/test/testROI.py62
-rw-r--r--src/silx/gui/plot/tools/test/testScatterProfileToolBar.py1
-rw-r--r--src/silx/gui/plot/tools/test/testTools.py1
-rw-r--r--src/silx/gui/plot/tools/toolbars.py1
-rw-r--r--src/silx/gui/plot/utils/__init__.py1
-rw-r--r--src/silx/gui/plot/utils/axis.py1
-rw-r--r--src/silx/gui/plot/utils/intersections.py1
-rw-r--r--src/silx/gui/plot3d/ParamTreeView.py3
-rw-r--r--src/silx/gui/plot3d/Plot3DWidget.py33
-rw-r--r--src/silx/gui/plot3d/Plot3DWindow.py3
-rw-r--r--src/silx/gui/plot3d/SFViewParamTree.py3
-rw-r--r--src/silx/gui/plot3d/ScalarFieldView.py3
-rw-r--r--src/silx/gui/plot3d/SceneWidget.py3
-rw-r--r--src/silx/gui/plot3d/SceneWindow.py3
-rw-r--r--src/silx/gui/plot3d/__init__.py2
-rw-r--r--src/silx/gui/plot3d/_model/__init__.py3
-rw-r--r--src/silx/gui/plot3d/_model/core.py3
-rw-r--r--src/silx/gui/plot3d/_model/items.py3
-rw-r--r--src/silx/gui/plot3d/_model/model.py3
-rw-r--r--src/silx/gui/plot3d/actions/Plot3DAction.py3
-rw-r--r--src/silx/gui/plot3d/actions/__init__.py1
-rw-r--r--src/silx/gui/plot3d/actions/io.py3
-rw-r--r--src/silx/gui/plot3d/actions/mode.py3
-rw-r--r--src/silx/gui/plot3d/actions/viewpoint.py3
-rw-r--r--src/silx/gui/plot3d/items/__init__.py3
-rw-r--r--src/silx/gui/plot3d/items/_pick.py3
-rw-r--r--src/silx/gui/plot3d/items/clipplane.py3
-rw-r--r--src/silx/gui/plot3d/items/core.py3
-rw-r--r--src/silx/gui/plot3d/items/image.py3
-rw-r--r--src/silx/gui/plot3d/items/mesh.py3
-rw-r--r--src/silx/gui/plot3d/items/mixins.py1
-rw-r--r--src/silx/gui/plot3d/items/scatter.py3
-rw-r--r--src/silx/gui/plot3d/items/volume.py3
-rw-r--r--src/silx/gui/plot3d/scene/__init__.py1
-rw-r--r--src/silx/gui/plot3d/scene/axes.py3
-rw-r--r--src/silx/gui/plot3d/scene/camera.py3
-rw-r--r--src/silx/gui/plot3d/scene/core.py3
-rw-r--r--src/silx/gui/plot3d/scene/cutplane.py3
-rw-r--r--src/silx/gui/plot3d/scene/event.py3
-rw-r--r--src/silx/gui/plot3d/scene/function.py3
-rw-r--r--src/silx/gui/plot3d/scene/interaction.py3
-rw-r--r--src/silx/gui/plot3d/scene/primitives.py3
-rw-r--r--src/silx/gui/plot3d/scene/test/__init__.py1
-rw-r--r--src/silx/gui/plot3d/scene/test/test_transform.py3
-rw-r--r--src/silx/gui/plot3d/scene/test/test_utils.py3
-rw-r--r--src/silx/gui/plot3d/scene/text.py3
-rw-r--r--src/silx/gui/plot3d/scene/transform.py3
-rw-r--r--src/silx/gui/plot3d/scene/utils.py3
-rw-r--r--src/silx/gui/plot3d/scene/viewport.py3
-rw-r--r--src/silx/gui/plot3d/scene/window.py3
-rw-r--r--src/silx/gui/plot3d/setup.py50
-rw-r--r--src/silx/gui/plot3d/test/__init__.py1
-rw-r--r--src/silx/gui/plot3d/test/testGL.py4
-rw-r--r--src/silx/gui/plot3d/test/testScalarFieldView.py4
-rw-r--r--src/silx/gui/plot3d/test/testSceneWidget.py4
-rw-r--r--src/silx/gui/plot3d/test/testSceneWidgetPicking.py4
-rw-r--r--src/silx/gui/plot3d/test/testSceneWindow.py5
-rw-r--r--src/silx/gui/plot3d/test/testStatsWidget.py4
-rw-r--r--src/silx/gui/plot3d/tools/GroupPropertiesWidget.py3
-rw-r--r--src/silx/gui/plot3d/tools/PositionInfoWidget.py3
-rw-r--r--src/silx/gui/plot3d/tools/ViewpointTools.py3
-rw-r--r--src/silx/gui/plot3d/tools/__init__.py1
-rw-r--r--src/silx/gui/plot3d/tools/test/__init__.py1
-rw-r--r--src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py1
-rw-r--r--src/silx/gui/plot3d/tools/toolbars.py3
-rw-r--r--src/silx/gui/plot3d/utils/__init__.py1
-rw-r--r--src/silx/gui/plot3d/utils/mng.py3
-rw-r--r--src/silx/gui/printer.py3
-rw-r--r--src/silx/gui/qt/__init__.py6
-rw-r--r--src/silx/gui/qt/_pyqt6.py64
-rw-r--r--src/silx/gui/qt/_pyside_dynamic.py1
-rw-r--r--src/silx/gui/qt/_qt.py113
-rw-r--r--src/silx/gui/qt/_utils.py14
-rw-r--r--src/silx/gui/qt/inspect.py16
-rw-r--r--src/silx/gui/setup.py55
-rw-r--r--src/silx/gui/test/__init__.py1
-rwxr-xr-xsrc/silx/gui/test/test_colors.py3
-rw-r--r--src/silx/gui/test/test_console.py3
-rw-r--r--src/silx/gui/test/test_icons.py1
-rw-r--r--src/silx/gui/test/test_qt.py1
-rw-r--r--src/silx/gui/test/utils.py3
-rwxr-xr-xsrc/silx/gui/utils/__init__.py1
-rw-r--r--src/silx/gui/utils/concurrent.py3
-rw-r--r--src/silx/gui/utils/glutils/__init__.py34
-rw-r--r--src/silx/gui/utils/image.py8
-rw-r--r--src/silx/gui/utils/matplotlib.py106
-rw-r--r--src/silx/gui/utils/projecturl.py3
-rwxr-xr-xsrc/silx/gui/utils/qtutils.py1
-rw-r--r--src/silx/gui/utils/signal.py1
-rwxr-xr-xsrc/silx/gui/utils/test/__init__.py1
-rw-r--r--src/silx/gui/utils/test/test.py3
-rw-r--r--src/silx/gui/utils/test/test_async.py3
-rw-r--r--src/silx/gui/utils/test/test_glutils.py33
-rw-r--r--src/silx/gui/utils/test/test_image.py1
-rwxr-xr-xsrc/silx/gui/utils/test/test_qtutils.py3
-rw-r--r--src/silx/gui/utils/test/test_testutils.py1
-rw-r--r--src/silx/gui/utils/testutils.py33
-rw-r--r--src/silx/gui/widgets/BoxLayoutDockWidget.py1
-rw-r--r--src/silx/gui/widgets/ColormapNameComboBox.py3
-rw-r--r--src/silx/gui/widgets/ElidedLabel.py42
-rw-r--r--src/silx/gui/widgets/FloatEdit.py3
-rw-r--r--src/silx/gui/widgets/FlowLayout.py3
-rw-r--r--src/silx/gui/widgets/FormGridLayout.py74
-rw-r--r--src/silx/gui/widgets/FrameBrowser.py1
-rw-r--r--src/silx/gui/widgets/HierarchicalTableView.py1
-rwxr-xr-xsrc/silx/gui/widgets/LegendIconWidget.py1
-rw-r--r--src/silx/gui/widgets/MedianFilterDialog.py1
-rw-r--r--src/silx/gui/widgets/MultiModeAction.py1
-rw-r--r--src/silx/gui/widgets/PeriodicTable.py1
-rw-r--r--src/silx/gui/widgets/PrintGeometryDialog.py1
-rw-r--r--src/silx/gui/widgets/PrintPreview.py1
-rw-r--r--src/silx/gui/widgets/RangeSlider.py2
-rw-r--r--src/silx/gui/widgets/TableWidget.py1
-rw-r--r--src/silx/gui/widgets/ThreadPoolPushButton.py1
-rw-r--r--src/silx/gui/widgets/WaitingPushButton.py1
-rw-r--r--src/silx/gui/widgets/__init__.py1
-rw-r--r--src/silx/gui/widgets/setup.py41
-rw-r--r--src/silx/gui/widgets/test/__init__.py1
-rw-r--r--src/silx/gui/widgets/test/test_boxlayoutdockwidget.py1
-rw-r--r--src/silx/gui/widgets/test/test_elidedlabel.py32
-rw-r--r--src/silx/gui/widgets/test/test_flowlayout.py1
-rw-r--r--src/silx/gui/widgets/test/test_framebrowser.py1
-rw-r--r--src/silx/gui/widgets/test/test_hierarchicaltableview.py1
-rw-r--r--src/silx/gui/widgets/test/test_legendiconwidget.py1
-rw-r--r--src/silx/gui/widgets/test/test_periodictable.py1
-rw-r--r--src/silx/gui/widgets/test/test_printpreview.py1
-rw-r--r--src/silx/gui/widgets/test/test_rangeslider.py1
-rw-r--r--src/silx/gui/widgets/test/test_tablewidget.py1
-rw-r--r--src/silx/gui/widgets/test/test_threadpoolpushbutton.py1
-rw-r--r--src/silx/image/__init__.py1
-rw-r--r--src/silx/image/_boundingbox.py1
-rw-r--r--src/silx/image/backprojection.py1
-rw-r--r--src/silx/image/bilinear.pyx1
-rw-r--r--src/silx/image/marchingsquares/__init__.py1
-rw-r--r--src/silx/image/marchingsquares/_mergeimpl.pyx1
-rw-r--r--src/silx/image/marchingsquares/_skimage.py1
-rw-r--r--src/silx/image/marchingsquares/setup.py51
-rw-r--r--src/silx/image/marchingsquares/test/__init__.py1
-rw-r--r--src/silx/image/marchingsquares/test/test_funcapi.py1
-rw-r--r--src/silx/image/marchingsquares/test/test_mergeimpl.py1
-rw-r--r--src/silx/image/medianfilter.py1
-rw-r--r--src/silx/image/phantomgenerator.py1
-rw-r--r--src/silx/image/projection.py1
-rw-r--r--src/silx/image/reconstruction.py1
-rw-r--r--src/silx/image/setup.py47
-rw-r--r--src/silx/image/shapes.pyx1
-rw-r--r--src/silx/image/sift.py1
-rw-r--r--src/silx/image/test/__init__.py1
-rw-r--r--src/silx/image/test/test_bb.py1
-rw-r--r--src/silx/image/test/test_bilinear.py1
-rw-r--r--src/silx/image/test/test_medianfilter.py1
-rw-r--r--src/silx/image/test/test_shapes.py1
-rw-r--r--src/silx/image/test/test_tomography.py1
-rw-r--r--src/silx/image/tomography.py1
-rw-r--r--src/silx/image/utils.py1
-rw-r--r--src/silx/io/__init__.py1
-rw-r--r--src/silx/io/commonh5.py1
-rw-r--r--src/silx/io/convert.py1
-rw-r--r--src/silx/io/dictdump.py21
-rwxr-xr-xsrc/silx/io/fabioh5.py1
-rw-r--r--src/silx/io/fioh5.py1
-rw-r--r--src/silx/io/h5py_utils.py1
-rw-r--r--src/silx/io/nxdata/__init__.py1
-rw-r--r--src/silx/io/nxdata/_utils.py1
-rw-r--r--src/silx/io/nxdata/parse.py87
-rw-r--r--src/silx/io/nxdata/write.py1
-rw-r--r--src/silx/io/octaveh5.py1
-rw-r--r--src/silx/io/rawh5.py1
-rw-r--r--src/silx/io/setup.py87
-rw-r--r--src/silx/io/specfile.pyx1
-rw-r--r--src/silx/io/specfile/include/SpecFileCython.h1
-rw-r--r--src/silx/io/specfile/src/sflabel.c18
-rw-r--r--src/silx/io/specfile/src/sfmca.c5
-rw-r--r--src/silx/io/specfile_wrapper.pxd1
-rw-r--r--src/silx/io/specfilewrapper.py1
-rw-r--r--src/silx/io/spech5.py1
-rw-r--r--src/silx/io/spectoh5.py1
-rw-r--r--src/silx/io/test/__init__.py1
-rw-r--r--src/silx/io/test/test_commonh5.py1
-rw-r--r--src/silx/io/test/test_dictdump.py58
-rwxr-xr-xsrc/silx/io/test/test_fabioh5.py1
-rw-r--r--src/silx/io/test/test_fioh5.py1
-rw-r--r--src/silx/io/test/test_h5py_utils.py33
-rw-r--r--src/silx/io/test/test_nxdata.py147
-rw-r--r--src/silx/io/test/test_octaveh5.py1
-rw-r--r--src/silx/io/test/test_rawh5.py1
-rw-r--r--src/silx/io/test/test_specfile.py1
-rw-r--r--src/silx/io/test/test_specfilewrapper.py1
-rw-r--r--src/silx/io/test/test_spech5.py1
-rw-r--r--src/silx/io/test/test_spectoh5.py1
-rw-r--r--src/silx/io/test/test_url.py1
-rw-r--r--src/silx/io/test/test_utils.py35
-rw-r--r--src/silx/io/test/test_write_to_h5.py1
-rw-r--r--src/silx/io/url.py1
-rw-r--r--src/silx/io/utils.py24
-rw-r--r--src/silx/math/__init__.py1
-rw-r--r--src/silx/math/_colormap.pyx1
-rw-r--r--src/silx/math/calibration.py1
-rw-r--r--src/silx/math/chistogramnd.pyx37
-rw-r--r--src/silx/math/chistogramnd_lut.pyx10
-rw-r--r--src/silx/math/colormap.py1
-rw-r--r--src/silx/math/combo.pyx1
-rw-r--r--src/silx/math/fft/__init__.py1
-rw-r--r--src/silx/math/fft/basefft.py1
-rw-r--r--src/silx/math/fft/clfft.py3
-rw-r--r--src/silx/math/fft/cufft.py3
-rw-r--r--src/silx/math/fft/fft.py35
-rw-r--r--src/silx/math/fft/fftw.py215
-rw-r--r--src/silx/math/fft/npfft.py68
-rw-r--r--src/silx/math/fft/setup.py41
-rw-r--r--src/silx/math/fft/test/__init__.py1
-rw-r--r--src/silx/math/fft/test/test_fft.py148
-rw-r--r--src/silx/math/fit/__init__.py1
-rw-r--r--src/silx/math/fit/bgtheories.py1
-rw-r--r--src/silx/math/fit/filters.pyx1
-rw-r--r--src/silx/math/fit/filters_wrapper.pxd1
-rw-r--r--src/silx/math/fit/fitmanager.py1
-rw-r--r--src/silx/math/fit/fittheories.py1
-rw-r--r--src/silx/math/fit/fittheory.py1
-rw-r--r--src/silx/math/fit/functions.pyx1
-rw-r--r--src/silx/math/fit/functions_wrapper.pxd1
-rw-r--r--src/silx/math/fit/leastsq.py1
-rw-r--r--src/silx/math/fit/peaks.pyx2
-rw-r--r--src/silx/math/fit/peaks_wrapper.pxd1
-rw-r--r--src/silx/math/fit/setup.py85
-rw-r--r--src/silx/math/fit/test/__init__.py1
-rw-r--r--src/silx/math/fit/test/test_bgtheories.py1
-rw-r--r--src/silx/math/fit/test/test_filters.py1
-rw-r--r--src/silx/math/fit/test/test_fit.py1
-rw-r--r--src/silx/math/fit/test/test_fitmanager.py1
-rw-r--r--src/silx/math/fit/test/test_functions.py1
-rw-r--r--src/silx/math/fit/test/test_peaks.py1
-rw-r--r--src/silx/math/histogram.py1
-rw-r--r--src/silx/math/histogramnd/include/histogramnd_c.h74
-rw-r--r--src/silx/math/histogramnd/src/histogramnd_template.c62
-rw-r--r--src/silx/math/histogramnd_c.pxd37
-rw-r--r--src/silx/math/interpolate.pyx1
-rw-r--r--src/silx/math/marchingcubes.pyx1
-rw-r--r--src/silx/math/math_compatibility.pxd1
-rw-r--r--src/silx/math/mc.pxd1
-rw-r--r--src/silx/math/medianfilter/__init__.py1
-rw-r--r--src/silx/math/medianfilter/median_filter.pxd1
-rw-r--r--src/silx/math/medianfilter/medianfilter.pyx1
-rw-r--r--src/silx/math/medianfilter/setup.py59
-rw-r--r--src/silx/math/medianfilter/test/__init__.py1
-rw-r--r--src/silx/math/medianfilter/test/benchmark.py1
-rw-r--r--src/silx/math/medianfilter/test/test_medianfilter.py1
-rw-r--r--src/silx/math/setup.py99
-rw-r--r--src/silx/math/test/__init__.py1
-rw-r--r--src/silx/math/test/benchmark_combo.py3
-rw-r--r--src/silx/math/test/histo_benchmarks.py1
-rw-r--r--src/silx/math/test/test_HistogramndLut_nominal.py1
-rw-r--r--src/silx/math/test/test_calibration.py3
-rw-r--r--src/silx/math/test/test_colormap.py3
-rw-r--r--src/silx/math/test/test_combo.py3
-rw-r--r--src/silx/math/test/test_histogramnd_error.py1
-rw-r--r--src/silx/math/test/test_histogramnd_nominal.py1
-rw-r--r--src/silx/math/test/test_histogramnd_vs_np.py37
-rw-r--r--src/silx/math/test/test_interpolate.py1
-rw-r--r--src/silx/math/test/test_marchingcubes.py3
-rw-r--r--src/silx/opencl/__init__.py1
-rw-r--r--src/silx/opencl/backprojection.py3
-rw-r--r--src/silx/opencl/codec/byte_offset.py3
-rw-r--r--src/silx/opencl/codec/setup.py43
-rw-r--r--src/silx/opencl/codec/test/__init__.py1
-rw-r--r--src/silx/opencl/codec/test/test_byte_offset.py3
-rw-r--r--src/silx/opencl/common.py1
-rw-r--r--src/silx/opencl/convolution.py3
-rw-r--r--src/silx/opencl/image.py3
-rw-r--r--src/silx/opencl/linalg.py3
-rw-r--r--src/silx/opencl/medfilt.py3
-rw-r--r--src/silx/opencl/processing.py114
-rw-r--r--src/silx/opencl/projection.py3
-rw-r--r--src/silx/opencl/reconstruction.py3
-rw-r--r--src/silx/opencl/setup.py48
-rw-r--r--src/silx/opencl/sinofilter.py3
-rw-r--r--src/silx/opencl/sparse.py3
-rw-r--r--src/silx/opencl/statistics.py1
-rw-r--r--src/silx/opencl/test/__init__.py1
-rw-r--r--src/silx/opencl/test/test_addition.py1
-rw-r--r--src/silx/opencl/test/test_array_utils.py3
-rw-r--r--src/silx/opencl/test/test_backprojection.py3
-rw-r--r--src/silx/opencl/test/test_convolution.py3
-rw-r--r--src/silx/opencl/test/test_doubleword.py1
-rw-r--r--src/silx/opencl/test/test_image.py3
-rw-r--r--src/silx/opencl/test/test_kahan.py1
-rw-r--r--src/silx/opencl/test/test_linalg.py3
-rw-r--r--src/silx/opencl/test/test_medfilt.py3
-rw-r--r--src/silx/opencl/test/test_projection.py3
-rw-r--r--src/silx/opencl/test/test_sparse.py1
-rw-r--r--src/silx/opencl/test/test_stats.py1
-rw-r--r--src/silx/opencl/utils.py3
-rw-r--r--src/silx/resources/__init__.py1
-rw-r--r--src/silx/resources/gui/icons/add-shape-rotated-rectangle.pngbin0 -> 1263 bytes
-rw-r--r--src/silx/resources/gui/icons/add-shape-rotated-rectangle.svg2
-rw-r--r--src/silx/resources/gui/icons/scale-auto.pngbin0 -> 704 bytes
-rw-r--r--src/silx/resources/gui/icons/scale-auto.svg2
-rw-r--r--src/silx/resources/gui/icons/scale-fixed.pngbin0 -> 1163 bytes
-rw-r--r--src/silx/resources/gui/icons/scale-fixed.svg2
-rw-r--r--src/silx/setup.py54
-rw-r--r--src/silx/sx/__init__.py5
-rw-r--r--src/silx/sx/_plot.py1
-rw-r--r--src/silx/sx/_plot3d.py1
-rw-r--r--src/silx/test/__init__.py5
-rw-r--r--src/silx/test/test_resources.py1
-rw-r--r--src/silx/test/test_sx.py1
-rw-r--r--src/silx/test/test_version.py1
-rw-r--r--src/silx/test/utils.py1
-rw-r--r--src/silx/third_party/__init__.py1
-rw-r--r--src/silx/third_party/scipy_spatial.py3
-rw-r--r--src/silx/third_party/setup.py49
-rw-r--r--src/silx/utils/ExternalResources.py80
-rw-r--r--src/silx/utils/__init__.py1
-rw-r--r--src/silx/utils/_have_openmp.pxd1
-rw-r--r--src/silx/utils/array_like.py3
-rw-r--r--src/silx/utils/debug.py1
-rw-r--r--src/silx/utils/deprecation.py3
-rw-r--r--src/silx/utils/enum.py3
-rw-r--r--src/silx/utils/exceptions.py1
-rw-r--r--src/silx/utils/files.py1
-rw-r--r--src/silx/utils/html.py1
-rw-r--r--src/silx/utils/launcher.py1
-rwxr-xr-xsrc/silx/utils/number.py1
-rw-r--r--src/silx/utils/property.py3
-rw-r--r--src/silx/utils/proxy.py3
-rw-r--r--src/silx/utils/retry.py96
-rw-r--r--src/silx/utils/setup.py43
-rwxr-xr-xsrc/silx/utils/test/__init__.py1
-rw-r--r--src/silx/utils/test/test_array_like.py1
-rw-r--r--src/silx/utils/test/test_debug.py1
-rw-r--r--src/silx/utils/test/test_deprecation.py1
-rw-r--r--src/silx/utils/test/test_enum.py3
-rw-r--r--src/silx/utils/test/test_external_resources.py5
-rw-r--r--src/silx/utils/test/test_launcher.py1
-rw-r--r--src/silx/utils/test/test_launcher_command.py1
-rw-r--r--src/silx/utils/test/test_number.py1
-rw-r--r--src/silx/utils/test/test_proxy.py1
-rw-r--r--src/silx/utils/test/test_retry.py45
-rwxr-xr-xsrc/silx/utils/test/test_testutils.py1
-rw-r--r--src/silx/utils/test/test_weakref.py1
-rwxr-xr-xsrc/silx/utils/testutils.py1
-rw-r--r--src/silx/utils/weakref.py2
669 files changed, 3999 insertions, 3625 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4e99a93..160243a 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,10 +1,137 @@
Change Log
==========
-1.0.0: 2021/11/XX
+1.1.0: 2022/10/27
-----------------
-This the first version of `silx` supporting `PySide6` (for `Qt6`) and using `pytest` to run the tests.
+This is the last version of `silx` supporting Python 3.6 and `PySide2`.
+Next version will require Python >= 3.7
+
+This is the first version of `silx` supporting `PyQt6` (for `Qt6`).
+Please note that `PyQt6` >= v6.3.0 is required.
+
+* `silx view`:
+
+ * Improved wildcard support in filename and data path (PR #3663)
+ * Enabled plot grid by default for curve plots (PR #3667)
+ * Fixed refresh for content opened as `file.h5::/path` (PR #3665)
+
+* `silx.gui`:
+
+ * Added support of `PyQt6` >= 6.3.0 (PR #3655)
+ * Fixed `matplotlib`>=3.6.0 and `PySide6` support (PR #3639)
+ * Fixed `PySide6` >=6.2.2 support (PR #3581)
+ * Fixed Python 3.10 with `PyQt5` support (PR #3591)
+ * Fixed crashes on exit when deriving `QApplication` (PR #3588)
+ * Deprecated `PySide2` support (PR #3648)
+ * Fixed: raise exception early when using a version of `PyQt5` incompatible with Python 3.10 (PR #3694)
+
+ * `silx.gui.data`:
+
+ * Updated: Do not keep aspect ratio in `NXdata` image views when axes `@units` are different (PR #3660)
+ * `silx.gui.data.ArrayTableWidget`: Updated to edit without clearing previous data (PR #3686)
+ * `silx.gui.data.DataViewer`: Added `selectionChanged` signal (PR #3646)
+ * `silx.gui.data.Hdf5TableView`: Fixed for virtual datasets in the same file (PR #3572)
+
+ * `silx.gui.dialog.ColormapDialog`: Updated layout and presentation of the features (PR #3671, #3609)
+
+ * `silx.gui.hdf5`: Fixed issue with unsupported hdf5 entity (e.g. datatype) (PR #3643)
+
+ * `silx.gui.plot`:
+
+ * `silx.gui.plot.items`:
+
+ * Added `BandROI` item (PR #3680, #3702, #3707)
+ * Updated to take errorbars into account for item bounds (PR #3647)
+ * Fixed `ArcROI` display (PR #3617)
+ * Fixed error logs for scatter triangle visualisation with aligned points (PR #3644)
+
+ * `silx.gui.plot.MaskToolsWidget`: Changed mask load/save default directory (PR #3704)
+
+ * `silx.gui.plot.PlotWidget`:
+
+ * Fixed time axis with values outside of supported range ]0, 10000[ years (PR 3597)
+ * Fixed matplotlib backend replot failure under specific conditions (PR #3590)
+
+ * `silx.gui.PlotWidget`'s OpenGL backend:
+
+ * Added support of LaTex-like math syntax to text display (PR #3600)
+ * Updated text label background to be less transparent (PR #3593)
+ * Fixed dashed curve rendering (PR #3596)
+ * Fixed image rendering of arcsinh colormap for uint8 and uint16 data (PR #3604)
+ * Fixed rendering on some GPU (PR #3695)
+ * Fixed empty text support (PR #3701)
+ * Fixed: Avoid rendering when OpenGL version/extension check fails (PR #3707)
+
+ * `silx.gui.plot.PlotWindow`: Fixed management of DockWidgets when showing/hiding the `PlotWindow` (PR #3631)
+ * `silx.gui.plot.PositionInfo`: Improved picking (PR #3640)
+ * `silx.gui.plot.StackView`: Updated toolbar implementation (PR #3697)
+
+ * `silx.gui.plot.stats`: Fixed warnings when all data is outside the selected stats region (PR #3659)
+ * `silx.gui.plot.tools`:
+
+ * Added snapping to profile curve (PR #3640)
+ * Fixed handling of `disconnect` exception (PR #3692)
+ * Fixed label formatting for 2D profile tool (PR #3698)
+ * Fixed computation of the slice profile (PR #3708)
+
+ * `silx.gui.utils.glutils.isOpenGLAvailable`: Added possibility to check `AA_ShareOpenGLContexts` (PR #3688)
+ * `silx.gui.widgets.ElidedLabel`: Fixed API inherited from `QLabel` (PR #3650, #3707)
+
+* `silx.io`:
+
+ * `silx.io.dictdump`:
+
+ * Added "info" logs when an entity is not copied to the output HDF5 file `dicttoh5` (PR #3664)
+ * Added support of `pint` in `dicttoh5` and `dicttonx` (PR #3683)
+
+ * `silx.io.nxdata`:
+
+ * Updated `get_default` to be more permissive and follow `@default` recursively (PR #3662)
+ * Updated error dataset retrieval (PR #3657, #3672)
+
+ * `silx.io.specfile`:
+
+ * Fixed buffer overflow for too long motor or label (PR #3622)
+ * Fixed missing data if there is a trailing space in the mca array (PR #3612)
+
+ * `silx.io.utils.retry`: Added retry for generator functions (PR #3679)
+
+* `silx.math`:
+
+ * `silx.math.histogram`:
+
+ * Added support of `uint16` weights for LUT histogram (PR #3670)
+ * Fixed `Histogramnd` computation on arrays with more than 2**31-1 samples (PR #3599)
+
+ * `silx.math.fft`:
+
+ * Added `export_wisdom()` and `import_wisdom()` (PR #3623)
+ * Fixed normalization modes, notably account for regression in `pyfftw` normalization (PR #3625)
+ * Fixed avoid creating OpenCL/Cuda contexts when not needed (PR #3587)
+
+ * `silx.math.fit`: Updated documentation (PR #3582)
+
+* `silx.opencl`: Updated OpenCL profiling, fixed memory leak (PR #3690)
+
+* `silx.utils.ExternalResources`: Stored downloaded data checksum (PR #3580)
+
+* Miscellaneous:
+
+ * Added `SILX_INSTALL_REQUIRES_STRIP` build configuration environment variable (PR #3602)
+ * Added optional use of `sphinx_autodoc_typehints` to generate the documentation (PR #3668)
+ * Updated build and development tools to remove dependency to `distutils` and `numpy.distutils` (PR #3583, #3585, #3613, #3649, #3651, #3653, #3658, #3661, #3678)
+ * Updated Windows installer (PR #3642)
+ * Updated documentation (PR #3699, #3709)
+ * Updated after 1.0.0 release (PR #3560, #3569)
+ * Fixed tests and continuous integration (PR #3632, #3637, #3639, #3685)
+ * Fixed Debian/Ubuntu packaging (PR #3693)
+ * Cleaned-up Python 2 compatibility code (PR #3673)
+
+1.0.0: 2021/12/06
+-----------------
+
+This is the first version of `silx` supporting `PySide6` (for `Qt6`) and using `pytest` to run the tests.
* `silx view`:
diff --git a/LICENSE b/LICENSE
index 15172e7..c010dc3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,14 +1,3 @@
-The silx toolkit is a software library and one of its goals is not to impose any license to the end user.
-
-Silx follows the permissive MIT license although it may include contributions following other licenses not interfering with the previous goal. Detailed information can be found in the copyright file.
-
-Silx uses the Qt library for its graphical user interfaces.
-A word of caution is to be provided.
-If users develop and distribute software using modules accessing Qt by means of Riverbank Computing Qt bindings PyQt4 or PyQt5, those users will be conditioned by the license of their PyQt4/5 software (GPL or commercial).
-If the end user does not own a commercial license of PyQt4 or PyQt5 and wishes to be free of any distribution condition, (s)he should be able to use PySide2 because it uses the LGPL license.
-
-The MIT license follows:
-
Copyright (c) European Synchrotron Radiation Facility (ESRF)
Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/PKG-INFO b/PKG-INFO
index 43179ac..f953bc6 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,11 +1,11 @@
Metadata-Version: 2.1
Name: silx
-Version: 1.0.0
+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: UNKNOWN
+License: MIT
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
diff --git a/doc/source/Tutorials/io.rst b/doc/source/Tutorials/io.rst
index 41a0dd3..4c54bba 100644
--- a/doc/source/Tutorials/io.rst
+++ b/doc/source/Tutorials/io.rst
@@ -324,6 +324,34 @@ For example to process all top-level groups of an HDF5 file:
Note that the method with the `retry` decorator has to be idempotent
as it can be executed several times for one call.
+An equivalent decorator exists for context managers
+
+.. code-block:: python
+
+ import silx.io.h5py_utils
+
+ @silx.io.h5py_utils.retry_contextmanager()
+ def measurement_context(filename, name):
+ """The method will be entered again if
+ any HDF5 IO fails.
+ """
+ with silx.io.h5py_utils.File(filename) as h5file:
+ yield h5file[name]["measurement"]
+
+Generator functions need to have a `start_index` parameter
+
+.. code-block:: python
+
+ import silx.io.h5py_utils
+
+ @silx.io.h5py_utils.retry()
+ def iter_measurement(filename, names, start_index=0):
+ """The method will be iterated again if any HDF5
+ IO fails, possibly with a different start index.
+ """
+ with silx.io.h5py_utils.File(filename) as h5file:
+ for name in names[start_index:]:
+ yield h5file[name]["measurement"]
Additional resources
--------------------
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 7e173f3..2d242e9 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
# /*##########################################################################
-# Copyright (C) 2015-2019 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
@@ -32,25 +31,16 @@ autogenerated file.
All configuration values have a default; values that are commented out
serve to show the default."""
-import sys
+import importlib
import os
-import os.path
-import glob
-import subprocess
+import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
project = u'silx'
-try:
- import silx
- project_dir = os.path.abspath(os.path.join(__file__, "..", "..", ".."))
- build_dir = os.path.abspath(silx.__file__)
- if not build_dir.startswith(project_dir):
- raise RuntimeError("%s looks to come from the system. Fix your PYTHONPATH and restart sphinx." % project)
-except ImportError:
- raise RuntimeError("%s is not on the path. Fix your PYTHONPATH and restart sphinx." % project)
+import silx
# Disable deprecation warnings:
# It avoid to spam documentation logs with deprecation warnings.
@@ -79,9 +69,13 @@ extensions = [
'sphinxext-archive',
'snapshotqt_directive',
'nbsphinx'
-
]
+if importlib.util.find_spec('sphinx_autodoc_typehints'):
+ extensions.append('sphinx_autodoc_typehints')
+
+ always_document_param_types = True
+
autodoc_member_order = 'bysource'
# Add any paths that contain templates here, relative to this directory.
diff --git a/doc/source/ext/snapshotqt_directive.py b/doc/source/ext/snapshotqt_directive.py
index 84b3ac6..2a40992 100644
--- a/doc/source/ext/snapshotqt_directive.py
+++ b/doc/source/ext/snapshotqt_directive.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2004-2019 European Synchrotron Radiation Facility
@@ -31,7 +30,6 @@ Configuration variable in conf.py:
the documentation source directory (i.e., the directory of conf.py)
(default: '..').
"""
-from __future__ import absolute_import
__authors__ = ["H. Payno", "T. Vincent"]
__license__ = "MIT"
diff --git a/doc/source/ext/sphinxext-archive.py b/doc/source/ext/sphinxext-archive.py
index dc1c2c8..39004e0 100644
--- a/doc/source/ext/sphinxext-archive.py
+++ b/doc/source/ext/sphinxext-archive.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017 European Synchrotron Radiation Facility
diff --git a/doc/source/install.rst b/doc/source/install.rst
index 0841c2a..db86ac1 100644
--- a/doc/source/install.rst
+++ b/doc/source/install.rst
@@ -7,15 +7,15 @@ programming language.
This table summarizes the support matrix of silx:
-+------------+--------------+----------------------------+
-| System | Python vers. | Qt and its bindings |
-+------------+--------------+----------------------------+
-| `Windows`_ | 3.6-3.9 | PyQt5.6+, PySide2, PySide6 |
-+------------+--------------+----------------------------+
-| `MacOS`_ | 3.6-3.9 | PyQt5.6+, PySide2, PySide6 |
-+------------+--------------+----------------------------+
-| `Linux`_ | 3.6-3.9 | PyQt5.3+, PySide2, PySide6 |
-+------------+--------------+----------------------------+
++------------+--------------+-----------------------------+
+| System | Python vers. | Qt and its bindings |
++------------+--------------+-----------------------------+
+| `Windows`_ | 3.6-3.9 | PyQt5.6+, PySide6, PyQt6.3+ |
++------------+--------------+-----------------------------+
+| `MacOS`_ | 3.6-3.9 | PyQt5.6+, PySide6, PyQt6.3+ |
++------------+--------------+-----------------------------+
+| `Linux`_ | 3.6-3.9 | PyQt5.3+, PySide6, PyQt6.3+ |
++------------+--------------+-----------------------------+
For the description of *silx* dependencies, see the Dependencies_ section.
@@ -66,8 +66,8 @@ The mandatory dependencies are:
The GUI widgets depend on the following extra packages:
* A Qt binding: either `PyQt5 <https://riverbankcomputing.com/software/pyqt/intro>`_,
- `PySide2 <https://pypi.org/project/PySide2/>`_, or
- `PySide6 <https://pypi.org/project/PySide6/>`_
+ `PySide6 <https://pypi.org/project/PySide6/>`_ or
+ `PyQt6 <https://pypi.org/project/PyQt6/>`_
* `matplotlib <http://matplotlib.org/>`_
* `PyOpenGL <http://pyopengl.sourceforge.net/>`_
* `qt_console <https://pypi.org/project/qtconsole>`_
@@ -289,32 +289,46 @@ To set the environment variables, type on the command line:
Advanced build options
++++++++++++++++++++++
-In case you want more control over the build procedure, the build command is:
+Advanced options can be set through the following environment variables:
-.. code-block:: bash
-
- python setup.py build
+.. list-table::
+ :widths: 1 4
+ :header-rows: 1
-There are few advanced options to ``setup.py build``:
+ * - Environment variable
+ - Description
+ * - ``SILX_WITH_OPENMP``
+ - Whether or not to compile Cython code with OpenMP support (default: ``True`` except on macOS where it is ``False``)
+ * - ``SILX_FORCE_CYTHON``
+ - Whether or not to force re-generating the C/C++ source code from Cython files (default: ``False``).
+ * - ``SPECFILE_USE_GNU_SOURCE``
+ - Whether or not to use a cleaner locale independent implementation of :mod:`silx.io.specfile` by using `_GNU_SOURCE=1`
+ (default: ``False``; POSIX operating system only).
+ * - ``SILX_FULL_INSTALL_REQUIRES``
+ - Set it to put all dependencies as ``install_requires`` (For packaging purpose).
+ * - ``SILX_INSTALL_REQUIRES_STRIP``
+ - Comma-separated list of package names to remove from ``install_requires`` (For packaging purpose).
+.. note:: Boolean options are passed as ``True`` or ``False``.
-* ``--no-cython``: Prevent Cython (even if installed) from re-generating the C source code.
- Use the one provided by the development team.
-* ``--no-openmp``: Recompiles the Cython code without OpenMP support (default for MacOSX).
-* ``--openmp``: Recompiles the Cython code with OpenMP support (default for Windows and Linux).
-Package the build into a wheel and install it:
+Package the build into a wheel and install it (this requires to install the `build <https://pypa-build.readthedocs.io>`_ package):
.. code-block:: bash
- python setup.py bdist_wheel
+ python -m build --wheel
pip install dist/silx*.whl
To build the documentation, using `Sphinx <http://www.sphinx-doc.org/>`_:
.. code-block:: bash
- python setup.py build build_doc
+ pip install . # Make sure to install the same version as the source
+ sphinx-build doc/source/ build/html
+
+.. note::
+ To re-generate the example script screenshots, build the documentation with the
+ environment variable ``DIRECTIVE_SNAPSHOT_QT`` set to ``True``.
Testing
+++++++
diff --git a/doc/source/license.rst b/doc/source/license.rst
index 760be91..2e79bc3 100644
--- a/doc/source/license.rst
+++ b/doc/source/license.rst
@@ -4,6 +4,18 @@ License
The source code of *silx* is licensed under the `MIT <https://opensource.org/licenses/MIT>`_ license:
.. include:: ../../LICENSE
+ :literal:
+
+Note:
+ The silx toolkit is a software library and one of its goals is not to impose any license to the end user.
+
+ silx follows the permissive MIT license although it may include contributions following other licenses not interfering with the previous goal.
+ Detailed information can be found in the copyright file.
+
+ silx uses the Qt library for its graphical user interfaces.
+ A word of caution is to be provided.
+ If users develop and distribute software using modules accessing Qt by means of Riverbank Computing Qt bindings PyQt4 or PyQt5, those users will be conditioned by the license of their PyQt4/5 software (GPL or commercial).
+ If the end user does not own a commercial license of PyQt4 or PyQt5 and wishes to be free of any distribution condition, (s)he should be able to use PySide2 because it uses the LGPL license.
The following list provides the copyright and license of the different source files of the project:
diff --git a/doc/source/modules/gui/data/img/ArrayTableWidget.png b/doc/source/modules/gui/data/img/ArrayTableWidget.png
index e7bb2a9..138d189 100644
--- a/doc/source/modules/gui/data/img/ArrayTableWidget.png
+++ b/doc/source/modules/gui/data/img/ArrayTableWidget.png
Binary files differ
diff --git a/doc/source/modules/gui/data/img/DataViewer.png b/doc/source/modules/gui/data/img/DataViewer.png
index 7980de9..6d0da71 100644
--- a/doc/source/modules/gui/data/img/DataViewer.png
+++ b/doc/source/modules/gui/data/img/DataViewer.png
Binary files differ
diff --git a/doc/source/modules/gui/icons.rst b/doc/source/modules/gui/icons.rst
index cb8e044..238c724 100644
--- a/doc/source/modules/gui/icons.rst
+++ b/doc/source/modules/gui/icons.rst
@@ -49,6 +49,8 @@ Available icons
- add-shape-polygon
* - |add-shape-rectangle|
- add-shape-rectangle
+ * - |add-shape-rotated-rectangle|
+ - add-shape-rotated-rectangle
* - |add-shape-unknown|
- add-shape-unknown
* - |add-shape-vertical|
@@ -315,6 +317,10 @@ Available icons
- rotate-3d
* - |rudder|
- rudder
+ * - |scale-auto|
+ - scale-auto
+ * - |scale-fixed|
+ - scale-fixed
* - |selected|
- selected
* - |shape-circle-solid|
@@ -421,6 +427,7 @@ Available icons
.. |add-shape-point| image:: ../../../../src/silx/resources/gui/icons/add-shape-point.png
.. |add-shape-polygon| image:: ../../../../src/silx/resources/gui/icons/add-shape-polygon.png
.. |add-shape-rectangle| image:: ../../../../src/silx/resources/gui/icons/add-shape-rectangle.png
+.. |add-shape-rotated-rectangle| image:: ../../../../src/silx/resources/gui/icons/add-shape-rotated-rectangle.png
.. |add-shape-unknown| image:: ../../../../src/silx/resources/gui/icons/add-shape-unknown.png
.. |add-shape-vertical| image:: ../../../../src/silx/resources/gui/icons/add-shape-vertical.png
.. |add| image:: ../../../../src/silx/resources/gui/icons/add.png
@@ -554,6 +561,8 @@ Available icons
.. |rm| image:: ../../../../src/silx/resources/gui/icons/rm.png
.. |rotate-3d| image:: ../../../../src/silx/resources/gui/icons/rotate-3d.png
.. |rudder| image:: ../../../../src/silx/resources/gui/icons/rudder.png
+.. |scale-auto| image:: ../../../../src/silx/resources/gui/icons/scale-auto.png
+.. |scale-fixed| image:: ../../../../src/silx/resources/gui/icons/scale-fixed.png
.. |selected| image:: ../../../../src/silx/resources/gui/icons/selected.png
.. |shape-circle-solid| image:: ../../../../src/silx/resources/gui/icons/shape-circle-solid.png
.. |shape-circle| image:: ../../../../src/silx/resources/gui/icons/shape-circle.png
diff --git a/doc/source/modules/gui/plot/getting_started.rst b/doc/source/modules/gui/plot/getting_started.rst
index 1c29f23..f8d80ec 100644
--- a/doc/source/modules/gui/plot/getting_started.rst
+++ b/doc/source/modules/gui/plot/getting_started.rst
@@ -89,7 +89,7 @@ A Qt GUI script must have a QApplication initialised before creating widgets:
[...]
qapp.exec()
-Unless a Qt binding has already been loaded, :mod:`silx.gui.qt` uses one of the supported Qt bindings (PyQt5, PySide2, PySide6).
+Unless a Qt binding has already been loaded, :mod:`silx.gui.qt` uses one of the supported Qt bindings (PyQt5, PySide6, PyQt6).
If you prefer to choose the Qt binding yourself, import it before importing
a module from :mod:`silx.gui`:
diff --git a/doc/source/modules/gui/plot/img/BasicGridStatsWidget.png b/doc/source/modules/gui/plot/img/BasicGridStatsWidget.png
index 261909a..a468cc1 100644
--- a/doc/source/modules/gui/plot/img/BasicGridStatsWidget.png
+++ b/doc/source/modules/gui/plot/img/BasicGridStatsWidget.png
Binary files differ
diff --git a/doc/source/modules/gui/plot/img/BasicStatsWidget.png b/doc/source/modules/gui/plot/img/BasicStatsWidget.png
index b0d815d..6dbdd30 100644
--- a/doc/source/modules/gui/plot/img/BasicStatsWidget.png
+++ b/doc/source/modules/gui/plot/img/BasicStatsWidget.png
Binary files differ
diff --git a/doc/source/modules/gui/plot/img/LimitsToolBar.png b/doc/source/modules/gui/plot/img/LimitsToolBar.png
index b360fe0..2d96458 100644
--- a/doc/source/modules/gui/plot/img/LimitsToolBar.png
+++ b/doc/source/modules/gui/plot/img/LimitsToolBar.png
Binary files differ
diff --git a/doc/source/modules/gui/plot/img/ROIStatsWidget.png b/doc/source/modules/gui/plot/img/ROIStatsWidget.png
index 7a634fe..fae9d62 100644
--- a/doc/source/modules/gui/plot/img/ROIStatsWidget.png
+++ b/doc/source/modules/gui/plot/img/ROIStatsWidget.png
Binary files differ
diff --git a/doc/source/modules/gui/plot/img/logColorbar.png b/doc/source/modules/gui/plot/img/logColorbar.png
index 49282e7..31594e0 100644
--- a/doc/source/modules/gui/plot/img/logColorbar.png
+++ b/doc/source/modules/gui/plot/img/logColorbar.png
Binary files differ
diff --git a/doc/source/modules/gui/plot3d/img/SceneWidget.png b/doc/source/modules/gui/plot3d/img/SceneWidget.png
index 4ddc0a8..7ad6de4 100644
--- a/doc/source/modules/gui/plot3d/img/SceneWidget.png
+++ b/doc/source/modules/gui/plot3d/img/SceneWidget.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/FrameBrowser.png b/doc/source/modules/gui/widgets/img/FrameBrowser.png
index 1d4ebcf..6f46631 100644
--- a/doc/source/modules/gui/widgets/img/FrameBrowser.png
+++ b/doc/source/modules/gui/widgets/img/FrameBrowser.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/HorizontalSliderWithBrowser.png b/doc/source/modules/gui/widgets/img/HorizontalSliderWithBrowser.png
index 96edd3c..13a4ddf 100644
--- a/doc/source/modules/gui/widgets/img/HorizontalSliderWithBrowser.png
+++ b/doc/source/modules/gui/widgets/img/HorizontalSliderWithBrowser.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/PeriodicCombo.png b/doc/source/modules/gui/widgets/img/PeriodicCombo.png
index 7534805..bec3a78 100644
--- a/doc/source/modules/gui/widgets/img/PeriodicCombo.png
+++ b/doc/source/modules/gui/widgets/img/PeriodicCombo.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/PeriodicList.png b/doc/source/modules/gui/widgets/img/PeriodicList.png
index 74ce7d6..a695525 100644
--- a/doc/source/modules/gui/widgets/img/PeriodicList.png
+++ b/doc/source/modules/gui/widgets/img/PeriodicList.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/PeriodicTable.png b/doc/source/modules/gui/widgets/img/PeriodicTable.png
index bada39a..70039ab 100644
--- a/doc/source/modules/gui/widgets/img/PeriodicTable.png
+++ b/doc/source/modules/gui/widgets/img/PeriodicTable.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/RangeSlider.png b/doc/source/modules/gui/widgets/img/RangeSlider.png
index e7a1011..f552fb3 100644
--- a/doc/source/modules/gui/widgets/img/RangeSlider.png
+++ b/doc/source/modules/gui/widgets/img/RangeSlider.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/TableWidget.png b/doc/source/modules/gui/widgets/img/TableWidget.png
index a614ae7..959af57 100644
--- a/doc/source/modules/gui/widgets/img/TableWidget.png
+++ b/doc/source/modules/gui/widgets/img/TableWidget.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/ThreadPoolPushButton.png b/doc/source/modules/gui/widgets/img/ThreadPoolPushButton.png
index eb55b14..5d1af4c 100644
--- a/doc/source/modules/gui/widgets/img/ThreadPoolPushButton.png
+++ b/doc/source/modules/gui/widgets/img/ThreadPoolPushButton.png
Binary files differ
diff --git a/doc/source/modules/gui/widgets/img/WaitingPushButton.png b/doc/source/modules/gui/widgets/img/WaitingPushButton.png
index 97bd14a..5bfcdb8 100644
--- a/doc/source/modules/gui/widgets/img/WaitingPushButton.png
+++ b/doc/source/modules/gui/widgets/img/WaitingPushButton.png
Binary files differ
diff --git a/doc/source/modules/io/commonh5.rst b/doc/source/modules/io/commonh5.rst
new file mode 100644
index 0000000..9e62ccd
--- /dev/null
+++ b/doc/source/modules/io/commonh5.rst
@@ -0,0 +1,28 @@
+. currentmodule:: silx.io
+
+:mod:`commonh5`: Helpers for writing h5py-like API
+--------------------------------------------------
+
+.. automodule:: silx.io.commonh5
+
+Classes
++++++++
+
+.. autoclass:: Node
+ :members:
+
+.. autoclass:: File
+ :show-inheritance:
+ :members:
+
+.. autoclass:: Group
+ :show-inheritance:
+ :undoc-members:
+ :members: name, basename, file, attrs, h5py_class, parent,
+ get, keys, values, items, visit, visititems
+ :special-members: __getitem__, __len__, __contains__, __iter__
+ :exclude-members: add_node
+
+.. autoclass:: Dataset
+ :show-inheritance:
+ :members:
diff --git a/doc/source/modules/io/fioh5.rst b/doc/source/modules/io/fioh5.rst
index c901878..99498de 100644
--- a/doc/source/modules/io/fioh5.rst
+++ b/doc/source/modules/io/fioh5.rst
@@ -24,12 +24,4 @@ Classes
.. autoclass:: FioFile
-.. autoclass:: silx.io.commonh5.Group
- :show-inheritance:
- :undoc-members:
- :members: name, basename, file, attrs, h5py_class, parent,
- get, keys, values, items, visit, visititems
- :special-members: __getitem__, __len__, __contains__, __iter__
- :exclude-members: add_node
-
.. autofunction:: is_fiofile \ No newline at end of file
diff --git a/doc/source/modules/io/index.rst b/doc/source/modules/io/index.rst
index a511bef..b69a760 100644
--- a/doc/source/modules/io/index.rst
+++ b/doc/source/modules/io/index.rst
@@ -7,7 +7,8 @@
.. toctree::
:maxdepth: 1
-
+
+ commonh5.rst
configdict.rst
convert.rst
dictdump.rst
diff --git a/doc/source/modules/io/spech5.rst b/doc/source/modules/io/spech5.rst
index 61e0083..a9b8456 100644
--- a/doc/source/modules/io/spech5.rst
+++ b/doc/source/modules/io/spech5.rst
@@ -26,14 +26,6 @@ Classes
.. autoclass:: SpecH5Group
:show-inheritance:
-.. autoclass:: silx.io.commonh5.Group
- :show-inheritance:
- :undoc-members:
- :members: name, basename, file, attrs, h5py_class, parent,
- get, keys, values, items, visit, visititems
- :special-members: __getitem__, __len__, __contains__, __iter__
- :exclude-members: add_node
-
.. autoclass:: SpecH5Dataset
:show-inheritance:
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
index e65c163..729bdec 100644
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -8,16 +8,21 @@ Source code, pre-built binaries (aka Python wheels) for Windows, MacOS and
ManyLinux, Debian/Ubuntu packages of released versions are made available in the following places:
- `Wheels and source code on PyPi <https://pypi.org/project/silx/>`_
+- `Conda package on conda-forge channel <https://anaconda.org/conda-forge/silx>`_
+- Windows application installer `on github release page <https://github.com/silx-kit/silx/releases/latest/>`_ (available in the `Assets` at the bottom).
- `Documentation on silx.org <http://www.silx.org/doc/silx/latest/>`_
-- `Unofficial Debian/Ubuntu packages <https://github.com/silx-kit/silx/releases>`_
+- `Unofficial Debian/Ubuntu packages <https://github.com/silx-kit/silx/releases/latest>`_
- :doc:`changelog`
+|release| |version|
+
Nightly builds
--------------
Linux packages and documentation are automatically generated from the tip of the project's repository on a regular basis:
- `Debian 10 and Ubuntu20.04 packages <http://www.silx.org/pub/linux-repo/>`_
+- `Wheels and Windows application <https://silx.gitlab-pages.esrf.fr/bob/silx/>`_
- `Documentation <http://www.silx.org/doc/silx/dev/>`_
Project
diff --git a/doc/source/sample_code/img/plotUpdateImageFromGevent.png b/doc/source/sample_code/img/plotUpdateImageFromGevent.png
new file mode 100644
index 0000000..c0caec3
--- /dev/null
+++ b/doc/source/sample_code/img/plotUpdateImageFromGevent.png
Binary files differ
diff --git a/doc/source/sample_code/index.rst b/doc/source/sample_code/index.rst
index 0aade4c..c33d560 100644
--- a/doc/source/sample_code/index.rst
+++ b/doc/source/sample_code/index.rst
@@ -343,6 +343,10 @@ Sample code that illustrates some functionalities of :class:`~silx.gui.plot.Plot
In this example a thread calls submitToQtMainThread to update the curve
of a plot.
+ * - :download:`plotUpdateImageFromGevent.py <../../../examples/plotUpdateImageFromGevent.py>`
+ - .. image:: img/plotUpdateImageFromGevent.png
+ :width: 150px
+ - This script illustrates the update of a :class:`~silx.gui.plot.Plot2D` widget from a gevent coroutine.
* - :download:`plotUpdateImageFromThread.py <../../../examples/plotUpdateImageFromThread.py>`
- .. image:: img/plotUpdateImageFromThread.png
:width: 150px
diff --git a/doc/source/virtualenv.rst b/doc/source/virtualenv.rst
index 280c031..537336d 100644
--- a/doc/source/virtualenv.rst
+++ b/doc/source/virtualenv.rst
@@ -132,7 +132,7 @@ To test *silx*, open an interactive python console:
python
-If you don't have PyQt5, PySide2 or PySide6, run:
+If you don't have PyQt5, PySide6 or PyQt6, run:
.. code-block:: bash
diff --git a/examples/__init__.py b/examples/__init__.py
index 625d5fa..2387e3c 100644
--- a/examples/__init__.py
+++ b/examples/__init__.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
diff --git a/examples/colormapDialog.py b/examples/colormapDialog.py
index d389327..c3065f9 100644
--- a/examples/colormapDialog.py
+++ b/examples/colormapDialog.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
diff --git a/examples/compareImages.py b/examples/compareImages.py
index 3408a72..df2f40c 100644
--- a/examples/compareImages.py
+++ b/examples/compareImages.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/compositeline.py b/examples/compositeline.py
index 72398e6..2416495 100644
--- a/examples/compositeline.py
+++ b/examples/compositeline.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
@@ -26,7 +25,6 @@
"""
Example to show the use of markers to draw head and tail of lines.
"""
-from __future__ import division
__license__ = "MIT"
diff --git a/examples/customDataView.py b/examples/customDataView.py
index e02e577..8c2855d 100644
--- a/examples/customDataView.py
+++ b/examples/customDataView.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
diff --git a/examples/customHdf5TreeModel.py b/examples/customHdf5TreeModel.py
index ffc0220..c3bd45a 100644
--- a/examples/customHdf5TreeModel.py
+++ b/examples/customHdf5TreeModel.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
diff --git a/examples/customSilxView.py b/examples/customSilxView.py
index c240280..11fa8e4 100644
--- a/examples/customSilxView.py
+++ b/examples/customSilxView.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2020 European Synchrotron Radiation Facility
diff --git a/examples/dropZones.py b/examples/dropZones.py
index 6593bbb..48e1fc4 100644
--- a/examples/dropZones.py
+++ b/examples/dropZones.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2020 European Synchrotron Radiation Facility
@@ -31,8 +30,6 @@ It provides 2 URLs (corresponding to 2 datasets) that can be dragged to
either a :class:`PlotWidget` or a QLable displaying the URL information.
"""
-from __future__ import absolute_import
-
__authors__ = ["V. Valls"]
__license__ = "MIT"
__date__ = "25/01/2019"
diff --git a/examples/exampleBaseline.py b/examples/exampleBaseline.py
index b53b412..7c22f1d 100644
--- a/examples/exampleBaseline.py
+++ b/examples/exampleBaseline.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
diff --git a/examples/fftPlotAction.py b/examples/fftPlotAction.py
index f7c819f..d51a121 100755
--- a/examples/fftPlotAction.py
+++ b/examples/fftPlotAction.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2020 European Synchrotron Radiation Facility
diff --git a/examples/fileDialog.py b/examples/fileDialog.py
index fa11ed5..69d6a94 100644
--- a/examples/fileDialog.py
+++ b/examples/fileDialog.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
@@ -27,8 +26,6 @@
Example for the use of the ImageFileDialog.
"""
-from __future__ import absolute_import
-
__authors__ = ["V. Valls"]
__license__ = "MIT"
__date__ = "14/02/2018"
diff --git a/examples/findContours.py b/examples/findContours.py
index acf5199..5b4eaf4 100644
--- a/examples/findContours.py
+++ b/examples/findContours.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/hdf5widget.py b/examples/hdf5widget.py
index 82ce27d..f578660 100755
--- a/examples/hdf5widget.py
+++ b/examples/hdf5widget.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/icons.py b/examples/icons.py
index ff8410d..d30c57c 100644
--- a/examples/icons.py
+++ b/examples/icons.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/imageStack.py b/examples/imageStack.py
index 4c211b5..244ac40 100644
--- a/examples/imageStack.py
+++ b/examples/imageStack.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/imageview.py b/examples/imageview.py
index 40b5dff..2b3273e 100755
--- a/examples/imageview.py
+++ b/examples/imageview.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
@@ -33,7 +32,6 @@ To view an image file with the current installed silx library:
To get help:
``python examples/imageview.py -h``
"""
-from __future__ import division
__authors__ = ["T. Vincent"]
__license__ = "MIT"
diff --git a/examples/periodicTable.py b/examples/periodicTable.py
index fc3985f..31c2d46 100644
--- a/examples/periodicTable.py
+++ b/examples/periodicTable.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2004-2021 European Synchrotron Radiation Facility
diff --git a/examples/plot3dContextMenu.py b/examples/plot3dContextMenu.py
index 0802b29..2436b50 100644
--- a/examples/plot3dContextMenu.py
+++ b/examples/plot3dContextMenu.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
@@ -33,8 +32,6 @@ This is done by adding a custom context menu to the :class:`Plot3DWidget`:
For more information on context menus, see Qt documentation.
"""
-from __future__ import absolute_import, division, unicode_literals
-
__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "03/10/2017"
diff --git a/examples/plot3dSceneWindow.py b/examples/plot3dSceneWindow.py
index 436b121..9e20dd3 100644
--- a/examples/plot3dSceneWindow.py
+++ b/examples/plot3dSceneWindow.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
@@ -39,8 +38,6 @@ It features:
"""
-from __future__ import absolute_import
-
__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "17/11/2017"
diff --git a/examples/plot3dUpdateScatterFromThread.py b/examples/plot3dUpdateScatterFromThread.py
index a02fec6..a5b3df1 100644
--- a/examples/plot3dUpdateScatterFromThread.py
+++ b/examples/plot3dUpdateScatterFromThread.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2019 European Synchrotron Radiation Facility
diff --git a/examples/plotClearAction.py b/examples/plotClearAction.py
index 6f1823a..4237bf9 100644
--- a/examples/plotClearAction.py
+++ b/examples/plotClearAction.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2018-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotContextMenu.py b/examples/plotContextMenu.py
index bd1ad87..8eb9758 100644
--- a/examples/plotContextMenu.py
+++ b/examples/plotContextMenu.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
diff --git a/examples/plotCurveLegendWidget.py b/examples/plotCurveLegendWidget.py
index 98ba30b..d0bbca3 100644
--- a/examples/plotCurveLegendWidget.py
+++ b/examples/plotCurveLegendWidget.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2018-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotInteractiveImageROI.py b/examples/plotInteractiveImageROI.py
index 298f7af..e2cbe7e 100644
--- a/examples/plotInteractiveImageROI.py
+++ b/examples/plotInteractiveImageROI.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2018-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotItemsSelector.py b/examples/plotItemsSelector.py
index d7493ae..bdfc3bd 100755
--- a/examples/plotItemsSelector.py
+++ b/examples/plotItemsSelector.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2020 European Synchrotron Radiation Facility
diff --git a/examples/plotLimits.py b/examples/plotLimits.py
index 75440f4..052e802 100644
--- a/examples/plotLimits.py
+++ b/examples/plotLimits.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotProfile.py b/examples/plotProfile.py
index 40e199a..61fe53a 100644
--- a/examples/plotProfile.py
+++ b/examples/plotProfile.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2020 European Synchrotron Radiation Facility
diff --git a/examples/plotROIStats.py b/examples/plotROIStats.py
index e713592..53a45dc 100644
--- a/examples/plotROIStats.py
+++ b/examples/plotROIStats.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
diff --git a/examples/plotStats.py b/examples/plotStats.py
index 433088f..dc2dc5b 100644
--- a/examples/plotStats.py
+++ b/examples/plotStats.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotUpdateCurveFromThread.py b/examples/plotUpdateCurveFromThread.py
index 27dbf9b..8f8717b 100644
--- a/examples/plotUpdateCurveFromThread.py
+++ b/examples/plotUpdateCurveFromThread.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotUpdateImageFromGevent.py b/examples/plotUpdateImageFromGevent.py
new file mode 100644
index 0000000..453cbbd
--- /dev/null
+++ b/examples/plotUpdateImageFromGevent.py
@@ -0,0 +1,112 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# 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
+# 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 script illustrates the update of a :class:`~silx.gui.plot.Plot2D`
+widget from a gevent coroutine.
+"""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "05/09/2017"
+
+
+import threading
+import time
+import gevent
+import logging
+
+import numpy
+
+from silx.gui import qt
+from silx.gui.utils import concurrent
+from silx.gui.plot import Plot2D
+
+_logger = logging.getLogger(__name__)
+
+Nx = 150
+Ny = 50
+
+
+def process_gevent():
+ """Process gevent in case of QTimer triggering it."""
+ try:
+ gevent.sleep(0.01)
+ except Exception:
+ _logger.critical("Uncaught exception from gevent", exc_info=True)
+
+
+def update_image(plot2d):
+ """Update the image of a :class:`~sil.gui.plot.Plot2D`
+
+ :param plot2d: The Plot2D to update."""
+
+ pos = {'x0': 0, 'y0': 0}
+ while True:
+ gevent.sleep(0.01)
+
+ # Create image
+ # width of peak
+ sigma_x = 0.15
+ sigma_y = 0.25
+ # x and y positions
+ x = numpy.linspace(-1.5, 1.5, Nx)
+ y = numpy.linspace(-1.0, 1.0, Ny)
+ xv, yv = numpy.meshgrid(x, y)
+ signal = numpy.exp(- ((xv - pos['x0']) ** 2 / sigma_x ** 2
+ + (yv - pos['y0']) ** 2 / sigma_y ** 2))
+ # add noise
+ signal += 0.3 * numpy.random.random(size=signal.shape)
+ # random walk of center of peak ('drift')
+ pos['x0'] += 0.05 * (numpy.random.random() - 0.5)
+ pos['y0'] += 0.05 * (numpy.random.random() - 0.5)
+
+ plot2d.addImage(signal, resetzoom=False)
+
+
+def main():
+ global app
+ app = qt.QApplication([])
+
+ gevent_timer = qt.QTimer()
+ gevent_timer.start(10)
+ gevent_timer.timeout.connect(process_gevent)
+
+ # Create a Plot2D, set its limits and display it
+ plot2d = Plot2D()
+ plot2d.getIntensityHistogramAction().setVisible(True)
+ plot2d.setLimits(0, Nx, 0, Ny)
+ plot2d.getDefaultColormap().setVRange(0., 1.5)
+ plot2d.show()
+
+ # Create the thread that calls submitToQtMainThread
+ updater = gevent.spawn(update_image, plot2d)
+
+ app.exec()
+
+ updater.kill() # Stop updating the plot
+ updater.join()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/plotUpdateImageFromThread.py b/examples/plotUpdateImageFromThread.py
index de23d3f..63f04ab 100644
--- a/examples/plotUpdateImageFromThread.py
+++ b/examples/plotUpdateImageFromThread.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
diff --git a/examples/plotWidget.py b/examples/plotWidget.py
index 5d1f4b6..5cfe6cc 100644
--- a/examples/plotWidget.py
+++ b/examples/plotWidget.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
diff --git a/examples/printPreview.py b/examples/printPreview.py
index 7fe5480..1a96e29 100755
--- a/examples/printPreview.py
+++ b/examples/printPreview.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/scatterMask.py b/examples/scatterMask.py
index 839fa3a..4244baa 100644
--- a/examples/scatterMask.py
+++ b/examples/scatterMask.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
#
diff --git a/examples/scatterview.py b/examples/scatterview.py
index 5df11be..2afae80 100755
--- a/examples/scatterview.py
+++ b/examples/scatterview.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
@@ -26,7 +25,6 @@
"""
Example to show the use of :class:`~silx.gui.plot.ScatterView.ScatterView` widget.
"""
-from __future__ import division
__license__ = "MIT"
diff --git a/examples/shiftPlotAction.py b/examples/shiftPlotAction.py
index 6c5f8cf..5faa9ba 100755
--- a/examples/shiftPlotAction.py
+++ b/examples/shiftPlotAction.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/simplewidget.py b/examples/simplewidget.py
index 0a1c336..30b11d2 100755
--- a/examples/simplewidget.py
+++ b/examples/simplewidget.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
diff --git a/examples/stackView.py b/examples/stackView.py
index f857140..5cb9a1c 100644
--- a/examples/stackView.py
+++ b/examples/stackView.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/examples/syncPlotLocation.py b/examples/syncPlotLocation.py
index 83c1ade..66b787b 100644
--- a/examples/syncPlotLocation.py
+++ b/examples/syncPlotLocation.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
diff --git a/examples/syncaxis.py b/examples/syncaxis.py
index 2976231..166d931 100644
--- a/examples/syncaxis.py
+++ b/examples/syncaxis.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
diff --git a/examples/viewer3DVolume.py b/examples/viewer3DVolume.py
index 5b86199..001fe19 100644
--- a/examples/viewer3DVolume.py
+++ b/examples/viewer3DVolume.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2020 European Synchrotron Radiation Facility
@@ -29,8 +28,6 @@ an interactive cutting plane.
It can also be started without providing a file.
"""
-from __future__ import absolute_import, division, unicode_literals
-
__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "05/01/2017"
diff --git a/examples/writetoh5.py b/examples/writetoh5.py
index 5e89e48..86bf1ec 100644
--- a/examples/writetoh5.py
+++ b/examples/writetoh5.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2004-2017 European Synchrotron Radiation Facility
diff --git a/package/debian10/rules b/package/debian10/rules
index e56f801..3a4310d 100755
--- a/package/debian10/rules
+++ b/package/debian10/rules
@@ -10,6 +10,7 @@ export PYBUILD_AFTER_INSTALL=rm -rf {destdir}/usr/bin/
export PYBUILD_NAME=silx
export SPECFILE_USE_GNU_SOURCE=1
export SILX_FULL_INSTALL_REQUIRES=1
+export SILX_INSTALL_REQUIRES_STRIP=hdf5plugin
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
diff --git a/package/debian11/rules b/package/debian11/rules
index d086f63..57ff649 100755
--- a/package/debian11/rules
+++ b/package/debian11/rules
@@ -10,6 +10,7 @@ export PYBUILD_AFTER_INSTALL=rm -rf {destdir}/usr/bin/
export PYBUILD_NAME=silx
export SPECFILE_USE_GNU_SOURCE=1
export SILX_FULL_INSTALL_REQUIRES=1
+export SILX_INSTALL_REQUIRES_STRIP=hdf5plugin
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
diff --git a/package/windows/bootstrap-silx-view.py b/package/windows/bootstrap-silx-view.py
index f8da02d..9d17483 100644
--- a/package/windows/bootstrap-silx-view.py
+++ b/package/windows/bootstrap-silx-view.py
@@ -1,4 +1,3 @@
-# coding: utf-8
import logging
diff --git a/package/windows/bootstrap.py b/package/windows/bootstrap.py
index 3c6e887..06800ad 100644
--- a/package/windows/bootstrap.py
+++ b/package/windows/bootstrap.py
@@ -1,4 +1,3 @@
-# coding: utf-8
import logging
diff --git a/package/windows/create-installer.iss.template b/package/windows/create-installer.iss.template
index ffb6af4..3e913b9 100644
--- a/package/windows/create-installer.iss.template
+++ b/package/windows/create-installer.iss.template
@@ -9,7 +9,7 @@ AppSupportURL=https://github.com/silx-kit/silx
AppUpdatesURL=https://github.com/silx-kit/silx/releases
DefaultDirName={autopf}\silx
DefaultGroupName=silx
-LicenseFile=..\..\LICENSE
+LicenseFile=LICENSE
OutputDir=artifacts
OutputBaseFilename=silx-#Version-x64
Compression=lzma
diff --git a/package/windows/pyinstaller.spec b/package/windows/pyinstaller.spec
index 59b66c1..a0066cf 100644
--- a/package/windows/pyinstaller.spec
+++ b/package/windows/pyinstaller.spec
@@ -1,8 +1,12 @@
# -*- mode: python -*-
+import importlib.metadata
import os.path
from pathlib import Path
import shutil
import subprocess
+import sys
+
+import pkg_resources
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
@@ -127,6 +131,31 @@ def move_silx_view_exe():
move_silx_view_exe()
+# Generate license file from current Python env
+def create_license_file(filename: str):
+ import PyQt5.QtCore
+
+ with open(filename, 'w') as f:
+ f.write(f"""
+This is free software.
+
+This distribution of silx is provided under the
+GNU General Public License v3 (https://www.gnu.org/licenses/gpl-3.0.en.html) since it includes PyQt5.
+
+It includes mainy software packages with different licenses:
+
+- Python ({sys.version}): PSF license, https://www.python.org/
+- Qt ({PyQt5.QtCore.qVersion()}): GNU Lesser General Public License v3, https://www.qt.io/
+""")
+
+ for dist in sorted(pkg_resources.working_set, key=lambda d: d.key):
+ license = importlib.metadata.metadata(dist.key).get('License')
+ homepage = importlib.metadata.metadata(dist.key).get('Home-page')
+ info = ", ".join(info for info in (license, homepage) if info)
+ f.write(f"- {dist.project_name} ({importlib.metadata.version(dist.key)}): {info}\n")
+
+create_license_file('LICENSE')
+
# Run innosetup
def innosetup():
from silx import version
diff --git a/pyproject.toml b/pyproject.toml
index c92bf42..cc3b000 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[build-system]
requires = [
"wheel",
- "setuptools",
+ "setuptools<60.0.0",
"numpy>=1.12",
"Cython>=0.21.1"
]
diff --git a/qtdesigner_plugins/plot1dplugin.py b/qtdesigner_plugins/plot1dplugin.py
index 8bfdc50..d41a328 100644
--- a/qtdesigner_plugins/plot1dplugin.py
+++ b/qtdesigner_plugins/plot1dplugin.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/qtdesigner_plugins/plot2dplugin.py b/qtdesigner_plugins/plot2dplugin.py
index 31c7557..4d211c4 100644
--- a/qtdesigner_plugins/plot2dplugin.py
+++ b/qtdesigner_plugins/plot2dplugin.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
diff --git a/qtdesigner_plugins/plotwidgetplugin.py b/qtdesigner_plugins/plotwidgetplugin.py
index 5c92ebe..f1d0d0b 100644
--- a/qtdesigner_plugins/plotwidgetplugin.py
+++ b/qtdesigner_plugins/plotwidgetplugin.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2021 European Synchrotron Radiation Facility
diff --git a/qtdesigner_plugins/plotwindowplugin.py b/qtdesigner_plugins/plotwindowplugin.py
index 89e6c47..649bbf5 100644
--- a/qtdesigner_plugins/plotwindowplugin.py
+++ b/qtdesigner_plugins/plotwindowplugin.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2021 European Synchrotron Radiation Facility
diff --git a/requirements-dev.txt b/requirements-dev.txt
index b5eea71..9b5807a 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,9 +1,11 @@
# List of silx development dependencies
-# Those ARE NOT required for installation, at runtime or to build from source (except for the doc)
+# Those ARE NOT required for installation or at runtime
-r requirements.txt
+build # To build the project
wheel # To build wheels
Sphinx # To build the documentation in doc/
+sphinx-autodoc-typehints # For leveraging Python type hints from Sphinx
pillow # For loading images in documentation generation
nbsphinx # For converting ipynb in documentation
pandoc # For documentation Qt snapshot updates
diff --git a/requirements.txt b/requirements.txt
index 06f57e1..7e2e8be 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,11 +3,11 @@
--trusted-host www.silx.org
--find-links http://www.silx.org/pub/wheelhouse/
---only-binary numpy,h5py,scipy,PySide2,PyQt5,PySide6
+--only-binary numpy,h5py,scipy,PySide2,PyQt5,PySide6,PyQt6
# Required dependencies (from setup.py setup_requires and install_requires)
numpy >= 1.12
-setuptools
+setuptools < 60.0.0
Cython >= 0.21.1
h5py
fabio >= 0.9
@@ -21,4 +21,4 @@ PyOpenGL # For silx.gui.plot3d
python-dateutil # For silx.gui.plot
scipy # For silx.math.fit demo, silx.image.sift demo, silx.image.sift.test
Pillow # For silx.opencl.image.test
-PyQt5 # PySide2, PySide6 # For silx.gui
+PyQt5 # PySide6, PyQt6>=6.3 # For silx.gui
diff --git a/run_tests.py b/run_tests.py
index bc8efe8..19368b3 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -1,8 +1,7 @@
#!/usr/bin/env python3
-# coding: utf8
# /*##########################################################################
#
-# 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
@@ -35,12 +34,12 @@ __authors__ = ["Jérôme Kieffer", "Thomas Vincent"]
__date__ = "30/09/2020"
__license__ = "MIT"
-import distutils.util
+import importlib
import logging
import os
import subprocess
import sys
-import importlib
+import sysconfig
# Capture all default warnings
@@ -85,12 +84,6 @@ def get_project_name(root_dir):
def is_debug_python():
"""Returns true if the Python interpreter is in debug mode."""
- try:
- import sysconfig
- except ImportError: # pragma nocover
- # Python < 2.7
- import distutils.sysconfig as sysconfig
-
if sysconfig.get_config_var("Py_DEBUG"):
return True
@@ -106,7 +99,7 @@ def build_project(name, root_dir):
:param str root_dir: Root directory of the project
:return: The path to the directory were build was performed
"""
- platform = distutils.util.get_platform()
+ platform = sysconfig.get_platform()
architecture = "lib.%s-%i.%i" % (platform,
sys.version_info[0], sys.version_info[1])
if is_debug_python():
@@ -121,8 +114,10 @@ def build_project(name, root_dir):
home = os.path.join(root_dir, "build", architecture)
logger.warning("Building %s to %s", name, home)
- p = subprocess.Popen([sys.executable, "setup.py", "build"],
- shell=False, cwd=root_dir)
+ cmd = [sys.executable, "setup.py", "build"]
+ if home:
+ cmd += ["--build-lib", home]
+ p = subprocess.Popen(cmd, shell=False, cwd=root_dir)
logger.debug("subprocess ended with rc= %s", p.wait())
if os.path.isdir(home):
diff --git a/setup.py b/setup.py
index 045b9a0..527c072 100644
--- a/setup.py
+++ b/setup.py
@@ -1,8 +1,7 @@
#!/usr/bin/env python3
-# coding: utf8
# /*##########################################################################
#
-# 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
@@ -25,60 +24,39 @@
# ###########################################################################*/
__authors__ = ["Jérôme Kieffer", "Thomas Vincent"]
-__date__ = "06/05/2020"
+__date__ = "29/08/2022"
__license__ = "MIT"
import sys
import os
import platform
-import shutil
import logging
-import glob
-# io import has to be here also to fix a bug on Debian 7 with python2.7
-# Without this, the system io module is not loaded from numpy.distutils.
-# The silx.io module seems to be loaded instead.
-import io
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("silx.setup")
-from distutils.command.clean import clean as Clean
-from distutils.command.build import build as _build
-try:
- from setuptools import Command
- from setuptools.command.sdist import sdist
- try:
- from Cython.Build import build_ext
- logger.info("Use setuptools with cython")
- except ImportError:
- from setuptools.command.build_ext import build_ext
- logger.info("Use setuptools, cython is missing")
+try: # setuptools >=62.4.0
+ from setuptools.command.build import build as _build
except ImportError:
- try:
- from numpy.distutils.core import Command
- except ImportError:
- from distutils.core import Command
- from distutils.command.sdist import sdist
- try:
- from Cython.Build import build_ext
- logger.info("Use distutils with cython")
- except ImportError:
- from distutils.command.build_ext import build_ext
- logger.info("Use distutils, cython is missing")
+ from distutils.command.build import build as _build
+from setuptools import Command, Extension, find_packages
+from setuptools.command.sdist import sdist
+from setuptools.command.build_ext import build_ext
+
try:
- import sphinx
- import sphinx.util.console
- sphinx.util.console.color_terminal = lambda: False
- from sphinx.setup_command import BuildDoc
+ import numpy
except ImportError:
- sphinx = None
+ raise ImportError(
+ "To install this package, you must install numpy first\n"
+ "(See https://pypi.org/project/numpy)")
+
PROJECT = "silx"
if sys.version_info.major < 3:
logger.error(PROJECT + " no longer supports Python2")
-if "LANG" not in os.environ and sys.platform == "darwin" and sys.version_info[0] > 2:
+if "LANG" not in os.environ and sys.platform == "darwin":
print("""WARNING: the LANG environment variable is not defined,
an utf-8 LANG is mandatory to use setup.py, you may face unexpected UnicodeError.
export LANG=en_US.utf-8
@@ -100,7 +78,7 @@ def get_readme():
"""Returns content of README.rst file"""
dirname = os.path.dirname(os.path.abspath(__file__))
filename = os.path.join(dirname, "README.rst")
- with io.open(filename, "r", encoding="utf-8") as fp:
+ with open(filename, "r", encoding="utf-8") as fp:
long_description = fp.read()
return long_description
@@ -124,51 +102,6 @@ classifiers = ["Development Status :: 5 - Production/Stable",
"Topic :: Software Development :: Libraries :: Python Modules",
]
-########
-# Test #
-########
-
-
-class PyTest(Command):
- """Command to start tests running the script: run_tests.py"""
- user_options = []
-
- description = "Execute the unittests"
-
- def initialize_options(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self):
- import subprocess
- errno = subprocess.call([sys.executable, 'run_tests.py'])
- if errno != 0:
- raise SystemExit(errno)
-
-# ################### #
-# build_doc command #
-# ################### #
-
-
-if sphinx is None:
-
- class SphinxExpectedCommand(Command):
- """Command to inform that sphinx is missing"""
- user_options = []
-
- def initialize_options(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self):
- raise RuntimeError(
- 'Sphinx is required to build or test the documentation.\n'
- 'Please install Sphinx (http://www.sphinx-doc.org).')
-
class BuildMan(Command):
"""Command to build man pages"""
@@ -311,7 +244,6 @@ class BuildMan(Command):
# help2man expect a single executable file to extract the help
# we create it, execute it, and delete it at the end
- py3 = sys.version_info >= (3, 0)
try:
# create a launcher using the right python interpreter
script_name = os.path.join(workdir, target_name)
@@ -330,17 +262,6 @@ class BuildMan(Command):
synopsis = self.get_synopsis(module_name, env)
if synopsis:
command_line += ["-n", synopsis]
- if not py3:
- # Before Python 3.4, ArgParser --version was using
- # stderr to print the version
- command_line.append("--no-discard-stderr")
- # Then we dont know if the documentation will contains
- # durtty things
- succeeded = self.run_targeted_script(target_name, script_name, env, False)
- if not succeeded:
- logger.info("Error while generating man file for target '%s'.", target_name)
- self.run_targeted_script(target_name, script_name, env, True)
- raise RuntimeError("Fail to generate '%s' man documentation" % target_name)
p = subprocess.Popen(command_line, env=env)
status = p.wait()
@@ -354,116 +275,6 @@ class BuildMan(Command):
os.remove(script_name)
os.rmdir(workdir)
-
-if sphinx is not None:
-
- class BuildDocCommand(BuildDoc):
- """Command to build documentation using sphinx.
-
- Project should have already be built.
- """
-
- def run(self):
- # make sure the python path is pointing to the newly built
- # code so that the documentation is built on this and not a
- # previously installed version
-
- build = self.get_finalized_command('build')
- sys.path.insert(0, os.path.abspath(build.build_lib))
-
- # # Copy .ui files to the path:
- # dst = os.path.join(
- # os.path.abspath(build.build_lib), "silx", "gui")
- # if not os.path.isdir(dst):
- # os.makedirs(dst)
- # for i in os.listdir("gui"):
- # if i.endswith(".ui"):
- # src = os.path.join("gui", i)
- # idst = os.path.join(dst, i)
- # if not os.path.exists(idst):
- # shutil.copy(src, idst)
-
- # Build the Users Guide in HTML and TeX format
- for builder in ['html', 'latex']:
- self.builder = builder
- self.builder_target_dir = os.path.join(self.build_dir, builder)
- self.mkpath(self.builder_target_dir)
- BuildDoc.run(self)
- sys.path.pop(0)
-
- class BuildDocAndGenerateScreenshotCommand(BuildDocCommand):
-
- def run(self):
- old = os.environ.get('DIRECTIVE_SNAPSHOT_QT')
- os.environ['DIRECTIVE_SNAPSHOT_QT'] = 'True'
- BuildDocCommand.run(self)
- if old is not None:
- os.environ['DIRECTIVE_SNAPSHOT_QT'] = old
- else:
- del os.environ['DIRECTIVE_SNAPSHOT_QT']
-
-else:
- BuildDocCommand = SphinxExpectedCommand
- BuildDocAndGenerateScreenshotCommand = SphinxExpectedCommand
-
-# ################### #
-# test_doc command #
-# ################### #
-
-if sphinx is not None:
-
- class TestDocCommand(BuildDoc):
- """Command to test the documentation using sphynx doctest.
-
- http://www.sphinx-doc.org/en/1.4.8/ext/doctest.html
- """
-
- def run(self):
- # make sure the python path is pointing to the newly built
- # code so that the documentation is built on this and not a
- # previously installed version
-
- build = self.get_finalized_command('build')
- sys.path.insert(0, os.path.abspath(build.build_lib))
-
- # Build the Users Guide in HTML and TeX format
- for builder in ['doctest']:
- self.builder = builder
- self.builder_target_dir = os.path.join(self.build_dir, builder)
- self.mkpath(self.builder_target_dir)
- BuildDoc.run(self)
- sys.path.pop(0)
-
-else:
- TestDocCommand = SphinxExpectedCommand
-
-# ############################# #
-# numpy.distutils Configuration #
-# ############################# #
-
-
-def configuration(parent_package='', top_path=None):
- """Recursive construction of package info to be used in setup().
-
- See http://docs.scipy.org/doc/numpy/reference/distutils.html#numpy.distutils.misc_util.Configuration
- """
- try:
- from numpy.distutils.misc_util import Configuration
- except ImportError:
- raise ImportError(
- "To install this package, you must install numpy first\n"
- "(See https://pypi.org/project/numpy)")
- config = Configuration(None, parent_package, top_path)
- config.set_options(
- ignore_setup_xxx_py=True,
- assume_default_configuration=True,
- delegate_options_to_subpackages=True,
- quiet=True)
- config.add_subpackage(
- PROJECT, subpackage_path=os.path.join(
- os.path.abspath(os.path.dirname(__file__)), 'src', PROJECT))
- return config
-
# ############## #
# Compiler flags #
# ############## #
@@ -474,11 +285,11 @@ class Build(_build):
user_options = [
('no-openmp', None,
- "do not use OpenMP for compiled extension modules"),
+ "DEPRECATED: Instead, set the environment variable SILX_WITH_OPENMP to False"),
('openmp', None,
- "use OpenMP for the compiled extension modules"),
+ "DEPRECATED: Instead, set the environment variable SILX_WITH_OPENMP to True"),
('force-cython', None,
- "recompile all Cython extension modules"),
+ "DEPRECATED: Instead, set the environment variable SILX_FORCE_CYTHON to True"),
]
user_options.extend(_build.user_options)
@@ -493,8 +304,14 @@ class Build(_build):
def finalize_options(self):
_build.finalize_options(self)
+ if self.no_openmp is not None:
+ logger.warning("--no-openmp is deprecated: Instead, set the environment variable SILX_WITH_OPENMP to False")
+ if self.openmp is not None:
+ logger.warning("--openmp is deprecated: Instead, set the environment variable SILX_WITH_OPENMP to True")
+ if self.force_cython is not None:
+ logger.warning("--force-cython is deprecated: Instead, set the environment variable SILX_FORCE_CYTHON to True")
if not self.force_cython:
- self.force_cython = self._parse_env_as_bool("FORCE_CYTHON") is True
+ self.force_cython = self._parse_env_as_bool("SILX_FORCE_CYTHON") is True
self.finalize_openmp_options()
def _parse_env_as_bool(self, key):
@@ -521,20 +338,16 @@ class Build(_build):
elif self.no_openmp:
use_openmp = False
else:
- env_with_openmp = self._parse_env_as_bool("WITH_OPENMP")
+ env_with_openmp = self._parse_env_as_bool("SILX_WITH_OPENMP")
if env_with_openmp is not None:
use_openmp = env_with_openmp
else:
# Use it by default
use_openmp = True
- if use_openmp:
- if platform.system() == "Darwin":
- # By default Xcode5 & XCode6 do not support OpenMP, Xcode4 is OK.
- osx = tuple([int(i) for i in platform.mac_ver()[0].split(".")])
- if osx >= (10, 8):
- logger.warning("OpenMP support ignored. Your platform does not support it.")
- use_openmp = False
+ if use_openmp and platform.system() == "Darwin":
+ logger.warning("OpenMP support ignored. Your platform does not support it.")
+ use_openmp = False
# Remove attributes used by distutils parsing
# use 'use_openmp' instead
@@ -600,149 +413,11 @@ class BuildExt(build_ext):
# Avoid empty arg
ext.extra_link_args = [arg for arg in extra_link_args if arg]
- elif self.compiler.compiler_type == 'unix':
- # Avoids runtime symbol collision for manylinux1 platform
- # See issue #1070
- extern = 'extern "C" ' if ext.language == 'c++' else ''
- return_type = 'void' if sys.version_info[0] <= 2 else 'PyObject*'
-
- ext.extra_compile_args.append('-fvisibility=hidden')
-
- import numpy
- numpy_version = [int(i) for i in numpy.version.full_version.split(".", 2)[:2]]
- if numpy_version < [1, 16]:
- ext.extra_compile_args.append(
- '''-D'PyMODINIT_FUNC=%s__attribute__((visibility("default"))) %s ' ''' % (extern, return_type))
- else:
- ext.define_macros.append(
- ('PyMODINIT_FUNC',
- '%s__attribute__((visibility("default"))) %s ' % (extern, return_type)))
-
- def is_debug_interpreter(self):
- """
- Returns true if the script is executed with a debug interpreter.
-
- It looks to be a non-standard code. It is not working for Windows and
- Mac. But it have to work at least for Debian interpreters.
-
- :rtype: bool
- """
- if sys.version_info >= (3, 0):
- # It is normalized on Python 3
- # But it is not available on Windows CPython
- if hasattr(sys, "abiflags"):
- return "d" in sys.abiflags
- else:
- # It's a Python 2 interpreter
- # pydebug is not available on Windows/Mac OS interpreters
- if hasattr(sys, "pydebug"):
- return sys.pydebug
-
- # We can't know if we uses debug interpreter
- return False
-
- def patch_compiler(self):
- """
- Patch the compiler to:
- - always compile extensions with debug symboles (-g)
- - only compile asserts in debug mode (-DNDEBUG)
-
- Plus numpy.distutils/setuptools/distutils inject a lot of duplicated
- flags. This function tries to clean up default debug options.
- """
- build_obj = self.distribution.get_command_obj("build")
- if build_obj.debug:
- debug_mode = build_obj.debug
- else:
- # Force debug_mode also when it uses python-dbg
- # It is needed for Debian packaging
- debug_mode = self.is_debug_interpreter()
-
- if self.compiler.compiler_type == "unix":
- args = list(self.compiler.compiler_so)
- # clean up debug flags -g is included later in another way
- must_be_cleaned = ["-DNDEBUG", "-g"]
- args = filter(lambda x: x not in must_be_cleaned, args)
- args = list(args)
-
- # always insert symbols
- args.append("-g")
- # only strip asserts in release mode
- if not debug_mode:
- args.append('-DNDEBUG')
- # patch options
- self.compiler.compiler_so = list(args)
-
def build_extensions(self):
- self.patch_compiler()
for ext in self.extensions:
self.patch_extension(ext)
build_ext.build_extensions(self)
-################################################################################
-# Clean command
-################################################################################
-
-
-class CleanCommand(Clean):
- description = "Remove build artifacts from the source tree"
-
- def expand(self, path_list):
- """Expand a list of path using glob magic.
-
- :param list[str] path_list: A list of path which may contains magic
- :rtype: list[str]
- :returns: A list of path without magic
- """
- path_list2 = []
- for path in path_list:
- if glob.has_magic(path):
- iterator = glob.iglob(path)
- path_list2.extend(iterator)
- else:
- path_list2.append(path)
- return path_list2
-
- def find(self, path_list):
- """Find a file pattern if directories.
-
- Could be done using "**/*.c" but it is only supported in Python 3.5.
-
- :param list[str] path_list: A list of path which may contains magic
- :rtype: list[str]
- :returns: A list of path without magic
- """
- import fnmatch
- path_list2 = []
- for pattern in path_list:
- for root, _, filenames in os.walk('.'):
- for filename in fnmatch.filter(filenames, pattern):
- path_list2.append(os.path.join(root, filename))
- return path_list2
-
- def run(self):
- Clean.run(self)
-
- cython_files = self.find(["*.pyx"])
- cythonized_files = [path.replace(".pyx", ".c") for path in cython_files]
- cythonized_files += [path.replace(".pyx", ".cpp") for path in cython_files]
-
- # really remove the directories
- # and not only if they are empty
- to_remove = [self.build_base]
- to_remove = self.expand(to_remove)
- to_remove += cythonized_files
-
- if not self.dry_run:
- for path in to_remove:
- try:
- if os.path.isdir(path):
- shutil.rmtree(path)
- else:
- os.remove(path)
- logger.info("removing '%s'", path)
- except OSError:
- pass
################################################################################
# Debian source tree
@@ -811,16 +486,12 @@ class sdist_debian(sdist):
# ##### #
-def get_project_configuration(dry_run):
+def get_project_configuration():
"""Returns project arguments for setup"""
# Use installed numpy version as minimal required version
# This is useful for wheels to advertise the numpy version they were built with
- if dry_run:
- numpy_requested_version = ""
- else:
- from numpy.version import version as numpy_version
- numpy_requested_version = ">=%s" % numpy_version
- logger.info("Install requires: numpy %s", numpy_requested_version)
+ numpy_requested_version = ">=%s" % numpy.version.version
+ logger.info("Install requires: numpy %s", numpy_requested_version)
install_requires = [
# for most of the computation
@@ -832,14 +503,6 @@ def get_project_configuration(dry_run):
"fabio>=0.9",
]
- # Add Python 2.7 backports
- # Equivalent to but supported by old setuptools:
- # "enum34; python_version == '2.7'",
- # "futures; python_version == '2.7'",
- if sys.version_info[0] == 2:
- install_requires.append("enum34")
- install_requires.append("futures")
-
# extras requirements: target 'full' to install all dependencies at once
full_requires = [
# opencl
@@ -872,6 +535,14 @@ def get_project_configuration(dry_run):
if os.environ.get('SILX_FULL_INSTALL_REQUIRES') is not None:
install_requires += full_requires
+ # Set the SILX_INSTALL_REQUIRES_STRIP env. var. to a comma-separated
+ # list of package names to remove them from install_requires
+ install_requires_strip = os.environ.get('SILX_INSTALL_REQUIRES_STRIP')
+ if install_requires_strip is not None:
+ for package_name in install_requires_strip.split(','):
+ install_requires.remove(package_name)
+
+
package_data = {
# Resources files for silx
'silx.resources': [
@@ -897,76 +568,232 @@ def get_project_configuration(dry_run):
cmdclass = dict(
build=Build,
- test=PyTest,
- build_screenshots=BuildDocAndGenerateScreenshotCommand,
- build_doc=BuildDocCommand,
- test_doc=TestDocCommand,
build_ext=BuildExt,
build_man=BuildMan,
- clean=CleanCommand,
debian_src=sdist_debian)
- if dry_run:
- # DRY_RUN implies actions which do not require NumPy
- #
- # And they are required to succeed without Numpy for example when
- # pip is used to install silx when Numpy is not yet present in
- # the system.
- setup_kwargs = {}
- else:
- config = configuration()
- setup_kwargs = config.todict()
-
- setup_kwargs.update(name=PROJECT,
- version=get_version(),
- url="http://www.silx.org/",
- author="data analysis unit",
- author_email="silx@esrf.fr",
- classifiers=classifiers,
- description="Software library for X-ray data analysis",
- long_description=get_readme(),
- install_requires=install_requires,
- extras_require=extras_require,
- cmdclass=cmdclass,
- package_data=package_data,
- zip_safe=False,
- entry_points=entry_points,
- python_requires='>=3.5',
- )
- return setup_kwargs
-
-
-def setup_package():
- """Run setup(**kwargs)
-
- Depending on the command, it either runs the complete setup which depends on numpy,
- or a *dry run* setup with no dependency on numpy.
- """
-
- # Check if action requires build/install
- dry_run = len(sys.argv) == 1 or (len(sys.argv) >= 2 and (
- '--help' in sys.argv[1:] or
- sys.argv[1] in ('--help-commands', 'egg_info', '--version',
- 'clean', '--name')))
+ def silx_io_specfile_define_macros():
+ # Locale and platform management
+ if sys.platform == "win32":
+ return [('WIN32', None), ('SPECFILE_POSIX', None)]
+ elif os.name.lower().startswith('posix'):
+ # the best choice is to have _GNU_SOURCE defined
+ # as a compilation flag because that allows the
+ # use of strtod_l
+ use_gnu_source = os.environ.get("SPECFILE_USE_GNU_SOURCE", "False")
+ if use_gnu_source in ("True", "1"): # 1 was the initially supported value
+ return [('_GNU_SOURCE', 1)]
+ return [('SPECFILE_POSIX', None)]
+ else:
+ return []
+
+ ext_modules = [
+
+ # silx.image
+
+ Extension(
+ name='silx.image.bilinear',
+ sources=["src/silx/image/bilinear.pyx"],
+ language='c',
+ ),
+ Extension(
+ name='silx.image.marchingsquares._mergeimpl',
+ sources=['src/silx/image/marchingsquares/_mergeimpl.pyx'],
+ include_dirs=[
+ numpy.get_include(),
+ os.path.join(os.path.dirname(__file__), "src", "silx", "utils", "include")
+ ],
+ language='c++',
+ extra_link_args=['-fopenmp'],
+ extra_compile_args=['-fopenmp'],
+ ),
+ Extension(
+ name='silx.image.shapes',
+ sources=["src/silx/image/shapes.pyx"],
+ language='c',
+ ),
+
+ # silx.io
+
+ Extension(
+ name='silx.io.specfile',
+ sources=[
+ 'src/silx/io/specfile/src/sfheader.c',
+ 'src/silx/io/specfile/src/sfinit.c',
+ 'src/silx/io/specfile/src/sflists.c',
+ 'src/silx/io/specfile/src/sfdata.c',
+ 'src/silx/io/specfile/src/sfindex.c',
+ 'src/silx/io/specfile/src/sflabel.c',
+ 'src/silx/io/specfile/src/sfmca.c',
+ 'src/silx/io/specfile/src/sftools.c',
+ 'src/silx/io/specfile/src/locale_management.c',
+ 'src/silx/io/specfile.pyx',
+ ],
+ define_macros=silx_io_specfile_define_macros(),
+ include_dirs=['src/silx/io/specfile/include'],
+ language='c',
+ ),
+
+ # silx.math
+
+ Extension(
+ name='silx.math._colormap',
+ sources=["src/silx/math/_colormap.pyx"],
+ language='c',
+ include_dirs=[
+ 'src/silx/math/include',
+ numpy.get_include(),
+ ],
+ extra_link_args=['-fopenmp'],
+ extra_compile_args=['-fopenmp'],
+ ),
+ Extension(
+ name='silx.math.chistogramnd',
+ sources=[
+ 'src/silx/math/histogramnd/src/histogramnd_c.c',
+ 'src/silx/math/chistogramnd.pyx',
+ ],
+ include_dirs=[
+ 'src/silx/math/histogramnd/include',
+ numpy.get_include(),
+ ],
+ language='c',
+ ),
+ Extension(
+ name='silx.math.chistogramnd_lut',
+ sources=['src/silx/math/chistogramnd_lut.pyx'],
+ include_dirs=[
+ 'src/silx/math/histogramnd/include',
+ numpy.get_include(),
+ ],
+ language='c',
+ ),
+ Extension(
+ name='silx.math.combo',
+ sources=['src/silx/math/combo.pyx'],
+ include_dirs=['src/silx/math/include'],
+ language='c',
+ ),
+ Extension(
+ name='silx.math.interpolate',
+ sources=["src/silx/math/interpolate.pyx"],
+ language='c',
+ include_dirs=[
+ 'src/silx/math/include',
+ numpy.get_include(),
+ ],
+ extra_link_args=['-fopenmp'],
+ extra_compile_args=['-fopenmp'],
+ ),
+ Extension(
+ name='silx.math.marchingcubes',
+ sources=[
+ 'src/silx/math/marchingcubes/mc_lut.cpp',
+ 'src/silx/math/marchingcubes.pyx',
+ ],
+ include_dirs=[
+ 'src/silx/math/marchingcubes',
+ numpy.get_include(),
+ ],
+ language='c++',
+ ),
+ Extension(
+ name='silx.math.medianfilter.medianfilter',
+ sources=['src/silx/math/medianfilter/medianfilter.pyx'],
+ include_dirs=[
+ 'src/silx/math/medianfilter/include',
+ numpy.get_include(),
+ ],
+ language='c++',
+ extra_link_args=['-fopenmp'],
+ extra_compile_args=['-fopenmp'],
+ ),
+
+ # silx.math.fit
+
+ Extension(
+ name='silx.math.fit.filters',
+ sources=[
+ '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/filters.pyx',
+ ],
+ include_dirs=['src/silx/math/fit/filters/include'],
+ language='c',
+ ),
+ Extension(
+ name='silx.math.fit.functions',
+ sources=[
+ 'src/silx/math/fit/functions/src/funs.c',
+ 'src/silx/math/fit/functions.pyx',
+ ],
+ include_dirs=['src/silx/math/fit/functions/include'],
+ language='c',
+ ),
+ Extension(
+ name='silx.math.fit.peaks',
+ sources=[
+ 'src/silx/math/fit/peaks/src/peaks.c',
+ 'src/silx/math/fit/peaks.pyx',
+ ],
+ include_dirs=['src/silx/math/fit/peaks/include'],
+ language='c',
+ ),
+ ]
- if dry_run:
- # DRY_RUN implies actions which do not require dependencies, like NumPy
- try:
- from setuptools import setup
- logger.info("Use setuptools.setup")
- except ImportError:
- from distutils.core import setup
- logger.info("Use distutils.core.setup")
- else:
- try:
- from setuptools import setup
- except ImportError:
- from numpy.distutils.core import setup
- logger.info("Use numpy.distutils.setup")
+ # silx.third_party
+
+ if os.path.exists(os.path.join(
+ os.path.dirname(__file__), "src", "silx", "third_party", "_local")
+ ):
+ ext_modules.append(
+ Extension(
+ name='silx.third_party._local.scipy_spatial.qhull',
+ sources=[
+ 'src/silx/third_party/_local/scipy_spatial/qhull/src/' + fname for fname in (
+ 'geom2_r.c', 'geom_r.c', 'global_r.c', 'io_r.c', 'libqhull_r.c', 'mem_r.c',
+ 'merge_r.c', 'poly2_r.c', 'poly_r.c', 'qset_r.c', 'random_r.c', 'rboxlib_r.c',
+ 'stat_r.c', 'usermem_r.c', 'userprintf_rbox_r.c', 'userprintf_r.c', 'user_r.c'
+ )] + [
+ 'src/silx/third_party/_local/scipy_spatial/qhull.pyx',
+ ],
+ include_dirs=[numpy.get_include()],
+ )
+ )
- setup_kwargs = get_project_configuration(dry_run)
- setup(**setup_kwargs)
+ return dict(
+ name=PROJECT,
+ version=get_version(),
+ license="MIT",
+ url="http://www.silx.org/",
+ author="data analysis unit",
+ author_email="silx@esrf.fr",
+ classifiers=classifiers,
+ description="Software library for X-ray data analysis",
+ long_description=get_readme(),
+ install_requires=install_requires,
+ extras_require=extras_require,
+ python_requires='>=3.5',
+ cmdclass=cmdclass,
+ zip_safe=False,
+ entry_points=entry_points,
+ packages=find_packages(where='src', include=['silx*']) + ['silx.examples'],
+ package_dir={
+ "": "src",
+ "silx.examples": "examples",
+ },
+ ext_modules=ext_modules,
+ package_data=package_data,
+ data_files=[
+ ('silx/third_party/_local/scipy_spatial/qhull', ['src/silx/third_party/_local/scipy_spatial/qhull/COPYING.txt'])
+ ],
+ )
if __name__ == "__main__":
- setup_package()
+ from setuptools import setup
+
+ setup(**get_project_configuration())
diff --git a/silx.egg-info/PKG-INFO b/src/silx.egg-info/PKG-INFO
index 43179ac..f953bc6 100644
--- a/silx.egg-info/PKG-INFO
+++ b/src/silx.egg-info/PKG-INFO
@@ -1,11 +1,11 @@
Metadata-Version: 2.1
Name: silx
-Version: 1.0.0
+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: UNKNOWN
+License: MIT
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
diff --git a/silx.egg-info/SOURCES.txt b/src/silx.egg-info/SOURCES.txt
index 99516d4..6d81290 100644
--- a/silx.egg-info/SOURCES.txt
+++ b/src/silx.egg-info/SOURCES.txt
@@ -233,6 +233,7 @@ 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
@@ -311,6 +312,7 @@ 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
@@ -353,6 +355,7 @@ examples/plotProfile.py
examples/plotROIStats.py
examples/plotStats.py
examples/plotUpdateCurveFromThread.py
+examples/plotUpdateImageFromGevent.py
examples/plotUpdateImageFromThread.py
examples/plotWidget.py
examples/printPreview.py
@@ -407,22 +410,20 @@ qtdesigner_plugins/plot1dplugin.py
qtdesigner_plugins/plot2dplugin.py
qtdesigner_plugins/plotwidgetplugin.py
qtdesigner_plugins/plotwindowplugin.py
-silx.egg-info/PKG-INFO
-silx.egg-info/SOURCES.txt
-silx.egg-info/dependency_links.txt
-silx.egg-info/entry_points.txt
-silx.egg-info/not-zip-safe
-silx.egg-info/requires.txt
-silx.egg-info/top_level.txt
src/silx/__init__.py
src/silx/__main__.py
src/silx/_config.py
src/silx/_version.py
src/silx/conftest.py
-src/silx/setup.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/setup.py
src/silx/app/test_.py
src/silx/app/test/__init__.py
src/silx/app/test/test_convert.py
@@ -433,7 +434,6 @@ 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/setup.py
src/silx/app/view/utils.py
src/silx/app/view/test/__init__.py
src/silx/app/view/test/test_launcher.py
@@ -444,7 +444,6 @@ src/silx/gui/conftest.py
src/silx/gui/console.py
src/silx/gui/icons.py
src/silx/gui/printer.py
-src/silx/gui/setup.py
src/silx/gui/_glutils/Context.py
src/silx/gui/_glutils/FramebufferTexture.py
src/silx/gui/_glutils/OpenGLWidget.py
@@ -470,7 +469,6 @@ 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/setup.py
src/silx/gui/data/test/__init__.py
src/silx/gui/data/test/test_arraywidget.py
src/silx/gui/data/test/test_dataviewer.py
@@ -486,7 +484,6 @@ 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/setup.py
src/silx/gui/dialog/utils.py
src/silx/gui/dialog/test/__init__.py
src/silx/gui/dialog/test/test_colormapdialog.py
@@ -498,7 +495,6 @@ 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/setup.py
src/silx/gui/fit/test/__init__.py
src/silx/gui/fit/test/testBackgroundWidget.py
src/silx/gui/fit/test/testFitConfig.py
@@ -513,7 +509,6 @@ 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/setup.py
src/silx/gui/hdf5/test/__init__.py
src/silx/gui/hdf5/test/test_hdf5.py
src/silx/gui/plot/AlphaSlider.py
@@ -548,12 +543,10 @@ 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/setup.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/setup.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
@@ -583,6 +576,7 @@ 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
@@ -660,7 +654,6 @@ 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/setup.py
src/silx/gui/plot3d/_model/__init__.py
src/silx/gui/plot3d/_model/core.py
src/silx/gui/plot3d/_model/items.py
@@ -713,6 +706,7 @@ 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
@@ -744,6 +738,7 @@ 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
@@ -758,7 +753,6 @@ 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/setup.py
src/silx/gui/widgets/test/__init__.py
src/silx/gui/widgets/test/test_boxlayoutdockwidget.py
src/silx/gui/widgets/test/test_elidedlabel.py
@@ -779,7 +773,6 @@ src/silx/image/medianfilter.py
src/silx/image/phantomgenerator.py
src/silx/image/projection.py
src/silx/image/reconstruction.py
-src/silx/image/setup.py
src/silx/image/shapes.pyx
src/silx/image/sift.py
src/silx/image/tomography.py
@@ -787,7 +780,6 @@ 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/setup.py
src/silx/image/marchingsquares/include/patterns.h
src/silx/image/marchingsquares/test/__init__.py
src/silx/image/marchingsquares/test/test_funcapi.py
@@ -808,7 +800,6 @@ src/silx/io/fioh5.py
src/silx/io/h5py_utils.py
src/silx/io/octaveh5.py
src/silx/io/rawh5.py
-src/silx/io/setup.py
src/silx/io/specfile.pyx
src/silx/io/specfile_wrapper.pxd
src/silx/io/specfilewrapper.py
@@ -864,7 +855,6 @@ src/silx/math/interpolate.pyx
src/silx/math/marchingcubes.pyx
src/silx/math/math_compatibility.pxd
src/silx/math/mc.pxd
-src/silx/math/setup.py
src/silx/math/fft/__init__.py
src/silx/math/fft/basefft.py
src/silx/math/fft/clfft.py
@@ -872,7 +862,6 @@ 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/setup.py
src/silx/math/fft/test/__init__.py
src/silx/math/fft/test/test_fft.py
src/silx/math/fit/__init__.py
@@ -887,7 +876,6 @@ 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/setup.py
src/silx/math/fit/filters/include/filters.h
src/silx/math/fit/filters/src/smoothnd.c
src/silx/math/fit/filters/src/snip1d.c
@@ -916,7 +904,6 @@ 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/setup.py
src/silx/math/medianfilter/include/median_filter.hpp
src/silx/math/medianfilter/test/__init__.py
src/silx/math/medianfilter/test/benchmark.py
@@ -944,14 +931,12 @@ src/silx/opencl/medfilt.py
src/silx/opencl/processing.py
src/silx/opencl/projection.py
src/silx/opencl/reconstruction.py
-src/silx/opencl/setup.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/setup.py
src/silx/opencl/codec/test/__init__.py
src/silx/opencl/codec/test/test_byte_offset.py
src/silx/opencl/sift/__init__.py
@@ -959,7 +944,6 @@ 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/setup.py
src/silx/opencl/sift/sift.py
src/silx/opencl/sift/utils.py
src/silx/opencl/sift/test/__init__.py
@@ -1024,6 +1008,8 @@ 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
@@ -1291,6 +1277,10 @@ 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
@@ -1464,14 +1454,12 @@ 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/setup.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/setup.py
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
@@ -1516,7 +1504,6 @@ src/silx/utils/number.py
src/silx/utils/property.py
src/silx/utils/proxy.py
src/silx/utils/retry.py
-src/silx/utils/setup.py
src/silx/utils/testutils.py
src/silx/utils/weakref.py
src/silx/utils/include/silx_store_openmp.h
diff --git a/silx.egg-info/dependency_links.txt b/src/silx.egg-info/dependency_links.txt
index 8b13789..8b13789 100644
--- a/silx.egg-info/dependency_links.txt
+++ b/src/silx.egg-info/dependency_links.txt
diff --git a/silx.egg-info/entry_points.txt b/src/silx.egg-info/entry_points.txt
index 898d662..898d662 100644
--- a/silx.egg-info/entry_points.txt
+++ b/src/silx.egg-info/entry_points.txt
diff --git a/silx.egg-info/not-zip-safe b/src/silx.egg-info/not-zip-safe
index 8b13789..8b13789 100644
--- a/silx.egg-info/not-zip-safe
+++ b/src/silx.egg-info/not-zip-safe
diff --git a/silx.egg-info/requires.txt b/src/silx.egg-info/requires.txt
index 11d2418..11d2418 100644
--- a/silx.egg-info/requires.txt
+++ b/src/silx.egg-info/requires.txt
diff --git a/silx.egg-info/top_level.txt b/src/silx.egg-info/top_level.txt
index dbcf435..dbcf435 100644
--- a/silx.egg-info/top_level.txt
+++ b/src/silx.egg-info/top_level.txt
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
new file mode 100644
index 0000000..d07efe5
--- /dev/null
+++ b/src/silx/resources/gui/icons/add-shape-rotated-rectangle.png
Binary files differ
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
new file mode 100644
index 0000000..35c68b4
--- /dev/null
+++ b/src/silx/resources/gui/icons/scale-auto.png
Binary files differ
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
new file mode 100644
index 0000000..9e765c2
--- /dev/null
+++ b/src/silx/resources/gui/icons/scale-fixed.png
Binary files differ
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"