summaryrefslogtreecommitdiff
path: root/src/silx/gui
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@debian.org>2022-11-03 10:02:44 +0100
committerPicca Frédéric-Emmanuel <picca@debian.org>2022-11-03 10:02:44 +0100
commit1c380bfeff1e13a9f7d506460336659502ca052d (patch)
tree48081d47748d4563eeaa76662287eb19638c8591 /src/silx/gui
parent4e774db12d5ebe7a20eded6dd434a289e27999e5 (diff)
New upstream version 1.1.0+dfsg
Diffstat (limited to 'src/silx/gui')
-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
332 files changed, 2070 insertions, 1582 deletions
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