summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@debian.org>2018-03-04 10:20:27 +0100
committerPicca Frédéric-Emmanuel <picca@debian.org>2018-03-04 10:20:27 +0100
commit270d5ddc31c26b62379e3caa9044dd75ccc71847 (patch)
tree55c5bfc851dfce7172d335cd2405b214323e3caf /examples
parente19c96eff0c310c06c4f268c8b80cb33bd08996f (diff)
New upstream version 0.7.0+dfsg
Diffstat (limited to 'examples')
-rw-r--r--examples/animatedicons.py155
-rw-r--r--examples/colormapDialog.py272
-rw-r--r--examples/customDataView.py106
-rw-r--r--examples/customHdf5TreeModel.py3
-rwxr-xr-xexamples/fftPlotAction.py8
-rw-r--r--examples/fileDialog.py262
-rwxr-xr-xexamples/hdf5widget.py167
-rw-r--r--examples/icons.py102
-rwxr-xr-xexamples/imageview.py7
-rw-r--r--examples/plot3dSceneWindow.py197
-rw-r--r--examples/plotClearAction.py75
-rw-r--r--examples/plotContextMenu.py11
-rwxr-xr-xexamples/plotItemsSelector.py4
-rw-r--r--examples/plotUpdateFromThread.py6
-rw-r--r--examples/plotWidget.py8
-rwxr-xr-xexamples/printPreview.py11
-rwxr-xr-xexamples/shiftPlotAction.py6
-rwxr-xr-xexamples/simplewidget.py5
-rw-r--r--examples/stackView.py6
-rw-r--r--examples/syncaxis.py1
20 files changed, 1145 insertions, 267 deletions
diff --git a/examples/animatedicons.py b/examples/animatedicons.py
deleted file mode 100644
index 5a5cad6..0000000
--- a/examples/animatedicons.py
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/env python
-# 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.
-#
-# ###########################################################################*/
-"""
-Display available project icons using Qt.
-"""
-from silx.gui import qt
-import silx.gui.icons
-import functools
-
-
-class AnimatedToolButton(qt.QToolButton):
- """ToolButton which support animated icons"""
-
- def __init__(self, parent=None):
- super(AnimatedToolButton, self).__init__(parent)
- self.__animatedIcon = None
-
- def setIcon(self, icon):
- if isinstance(icon, silx.gui.icons.AbstractAnimatedIcon):
- self._setAnimatedIcon(icon)
- else:
- self._setAnimatedIcon(None)
- super(AnimatedToolButton, self).setIcon(icon)
-
- def _setAnimatedIcon(self, icon):
- if self.__animatedIcon is not None:
- self.__animatedIcon.unregister(self)
- self.__animatedIcon.iconChanged.disconnect(self.__updateIcon)
- self.__animatedIcon = icon
- if self.__animatedIcon is not None:
- self.__animatedIcon.register(self)
- self.__animatedIcon.iconChanged.connect(self.__updateIcon)
- i = self.__animatedIcon.currentIcon()
- else:
- i = qt.QIcon()
- super(AnimatedToolButton, self).setIcon(i)
-
- def __updateIcon(self, icon):
- super(AnimatedToolButton, self).setIcon(icon)
-
- def icon(self):
- if self.__animatedIcon is not None:
- return self.__animatedIcon
- else:
- return super(AnimatedToolButton, self).icon()
-
-
-class AnimatedIconPreview(qt.QMainWindow):
-
- def __init__(self, *args, **kwargs):
- qt.QMainWindow.__init__(self, *args, **kwargs)
-
- widget = qt.QWidget(self)
- self.iconPanel = self.createIconPanel(widget)
- self.sizePanel = self.createSizePanel(widget)
-
- layout = qt.QVBoxLayout(widget)
- layout.addWidget(self.sizePanel)
- layout.addWidget(self.iconPanel)
- layout.addStretch()
- self.setCentralWidget(widget)
-
- def createSizePanel(self, parent):
- group = qt.QButtonGroup()
- group.setExclusive(True)
- panel = qt.QWidget(parent)
- panel.setLayout(qt.QHBoxLayout())
-
- buttons = {}
- for size in [16, 24, 32]:
- button = qt.QPushButton("%spx" % size, panel)
- button.clicked.connect(functools.partial(self.setIconSize, size))
- button.setCheckable(True)
- panel.layout().addWidget(button)
- group.addButton(button)
- buttons[size] = button
-
- self.__sizeGroup = group
- buttons[24].setChecked(True)
- return panel
-
- def createIconPanel(self, parent):
- panel = qt.QWidget(parent)
- layout = qt.QVBoxLayout()
- panel.setLayout(layout)
-
- self.tools = []
-
- # wait icon
- icon = silx.gui.icons.getWaitIcon()
- tool = AnimatedToolButton(panel)
- tool.setIcon(icon)
- tool.setText("getWaitIcon")
- tool.setToolButtonStyle(qt.Qt.ToolButtonTextBesideIcon)
- self.tools.append(tool)
-
- icon = silx.gui.icons.getAnimatedIcon("process-working")
- tool = AnimatedToolButton(panel)
- tool.setIcon(icon)
- tool.setText("getAnimatedIcon")
- tool.setToolButtonStyle(qt.Qt.ToolButtonTextBesideIcon)
- self.tools.append(tool)
-
- icon = silx.gui.icons.MovieAnimatedIcon("process-working", self)
- tool = AnimatedToolButton(panel)
- tool.setIcon(icon)
- tool.setText("MovieAnimatedIcon")
- tool.setToolButtonStyle(qt.Qt.ToolButtonTextBesideIcon)
- self.tools.append(tool)
-
- icon = silx.gui.icons.MultiImageAnimatedIcon("process-working", self)
- tool = AnimatedToolButton(panel)
- tool.setIcon(icon)
- tool.setText("MultiImageAnimatedIcon")
- tool.setToolButtonStyle(qt.Qt.ToolButtonTextBesideIcon)
- self.tools.append(tool)
-
- for t in self.tools:
- layout.addWidget(t)
-
- return panel
-
- def setIconSize(self, size):
- for tool in self.tools:
- tool.setIconSize(qt.QSize(size, size))
-
-
-if __name__ == "__main__":
- app = qt.QApplication([])
- window = AnimatedIconPreview()
- window.setVisible(True)
- app.exec_()
diff --git a/examples/colormapDialog.py b/examples/colormapDialog.py
new file mode 100644
index 0000000..c663591
--- /dev/null
+++ b/examples/colormapDialog.py
@@ -0,0 +1,272 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2017-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.
+#
+# ###########################################################################*/
+"""This script shows the features of a :mod:`~silx.gui.plot.ColormapDialog`.
+"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "19/01/2018"
+
+import functools
+import numpy
+
+try:
+ import scipy
+except ImportError:
+ scipy = None
+
+from silx.gui import qt
+from silx.gui.plot.ColormapDialog import ColormapDialog
+from silx.gui.plot.Colormap import Colormap
+from silx.gui.plot.ColorBar import ColorBarWidget
+
+
+class ColormapDialogExample(qt.QMainWindow):
+ """PlotWidget with an ad hoc toolbar and a colorbar"""
+
+ def __init__(self, parent=None):
+ super(ColormapDialogExample, self).__init__(parent)
+ self.setWindowTitle("Colormap dialog example")
+
+ self.colormap1 = Colormap("viridis")
+ self.colormap2 = Colormap("gray")
+
+ self.colorBar = ColorBarWidget(self)
+
+ self.colorDialogs = []
+
+ options = qt.QWidget(self)
+ options.setLayout(qt.QVBoxLayout())
+ self.createOptions(options.layout())
+
+ mainWidget = qt.QWidget(self)
+ mainWidget.setLayout(qt.QHBoxLayout())
+ mainWidget.layout().addWidget(options)
+ mainWidget.layout().addWidget(self.colorBar)
+ self.mainWidget = mainWidget
+
+ self.setCentralWidget(mainWidget)
+ self.createColorDialog()
+
+ def createOptions(self, layout):
+ button = qt.QPushButton("Create a new dialog")
+ button.clicked.connect(self.createColorDialog)
+ layout.addWidget(button)
+
+ layout.addSpacing(10)
+
+ button = qt.QPushButton("Set editable")
+ button.clicked.connect(self.setEditable)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set non-editable")
+ button.clicked.connect(self.setNonEditable)
+ layout.addWidget(button)
+
+ layout.addSpacing(10)
+
+ button = qt.QPushButton("Set no colormap")
+ button.clicked.connect(self.setNoColormap)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set colormap 1")
+ button.clicked.connect(self.setColormap1)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set colormap 2")
+ button.clicked.connect(self.setColormap2)
+ layout.addWidget(button)
+ button = qt.QPushButton("Create new colormap")
+ button.clicked.connect(self.setNewColormap)
+ layout.addWidget(button)
+
+ layout.addSpacing(10)
+
+ button = qt.QPushButton("Set no histogram")
+ button.clicked.connect(self.setNoHistogram)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set positive histogram")
+ button.clicked.connect(self.setPositiveHistogram)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set neg-pos histogram")
+ button.clicked.connect(self.setNegPosHistogram)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set negative histogram")
+ button.clicked.connect(self.setNegativeHistogram)
+ layout.addWidget(button)
+
+ layout.addSpacing(10)
+
+ button = qt.QPushButton("Set no range")
+ button.clicked.connect(self.setNoRange)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set positive range")
+ button.clicked.connect(self.setPositiveRange)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set neg-pos range")
+ button.clicked.connect(self.setNegPosRange)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set negative range")
+ button.clicked.connect(self.setNegativeRange)
+ layout.addWidget(button)
+
+ layout.addSpacing(10)
+
+ button = qt.QPushButton("Set no data")
+ button.clicked.connect(self.setNoData)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set shepp logan phantom")
+ button.clicked.connect(self.setSheppLoganPhantom)
+ layout.addWidget(button)
+ button = qt.QPushButton("Set data with non finite")
+ button.clicked.connect(self.setDataWithNonFinite)
+ layout.addWidget(button)
+
+ layout.addStretch()
+
+ def createColorDialog(self):
+ newDialog = ColormapDialog(self)
+ newDialog.finished.connect(functools.partial(self.removeColorDialog, newDialog))
+ self.colorDialogs.append(newDialog)
+ self.mainWidget.layout().addWidget(newDialog)
+
+ def removeColorDialog(self, dialog):
+ self.colorDialogs.remove(dialog)
+
+ def setNoColormap(self):
+ self.colorBar.setColormap(None)
+ for dialog in self.colorDialogs:
+ dialog.setColormap(None)
+
+ def setColormap1(self):
+ self.colorBar.setColormap(self.colormap1)
+ for dialog in self.colorDialogs:
+ dialog.setColormap(self.colormap1)
+
+ def setColormap2(self):
+ self.colorBar.setColormap(self.colormap2)
+ for dialog in self.colorDialogs:
+ dialog.setColormap(self.colormap2)
+
+ def setEditable(self):
+ for dialog in self.colorDialogs:
+ colormap = dialog.getColormap()
+ if colormap is not None:
+ colormap.setEditable(True)
+
+ def setNonEditable(self):
+ for dialog in self.colorDialogs:
+ colormap = dialog.getColormap()
+ if colormap is not None:
+ colormap.setEditable(False)
+
+ def setNewColormap(self):
+ self.colormap = Colormap("inferno")
+ self.colorBar.setColormap(self.colormap)
+ for dialog in self.colorDialogs:
+ dialog.setColormap(self.colormap)
+
+ def setNoHistogram(self):
+ for dialog in self.colorDialogs:
+ dialog.setHistogram()
+
+ def setPositiveHistogram(self):
+ histo = [5, 10, 50, 10, 5]
+ pos = 1
+ edges = list(range(pos, pos + len(histo)))
+ for dialog in self.colorDialogs:
+ dialog.setHistogram(histo, edges)
+
+ def setNegPosHistogram(self):
+ histo = [5, 10, 50, 10, 5]
+ pos = -2
+ edges = list(range(pos, pos + len(histo)))
+ for dialog in self.colorDialogs:
+ dialog.setHistogram(histo, edges)
+
+ def setNegativeHistogram(self):
+ histo = [5, 10, 50, 10, 5]
+ pos = -30
+ edges = list(range(pos, pos + len(histo)))
+ for dialog in self.colorDialogs:
+ dialog.setHistogram(histo, edges)
+
+ def setNoRange(self):
+ for dialog in self.colorDialogs:
+ dialog.setDataRange()
+
+ def setPositiveRange(self):
+ for dialog in self.colorDialogs:
+ dialog.setDataRange(1, 1, 10)
+
+ def setNegPosRange(self):
+ for dialog in self.colorDialogs:
+ dialog.setDataRange(-10, 1, 10)
+
+ def setNegativeRange(self):
+ for dialog in self.colorDialogs:
+ dialog.setDataRange(-10, float("nan"), -1)
+
+ def setNoData(self):
+ for dialog in self.colorDialogs:
+ dialog.setData(None)
+
+ def setSheppLoganPhantom(self):
+ from silx.image import phantomgenerator
+ data = phantomgenerator.PhantomGenerator.get2DPhantomSheppLogan(256)
+ data = data * 1000
+ if scipy is not None:
+ from scipy import ndimage
+ data = ndimage.gaussian_filter(data, sigma=20)
+ data = numpy.random.poisson(data)
+ self.data = data
+ for dialog in self.colorDialogs:
+ dialog.setData(data)
+
+ def setDataWithNonFinite(self):
+ from silx.image import phantomgenerator
+ data = phantomgenerator.PhantomGenerator.get2DPhantomSheppLogan(256)
+ data = data * 1000
+ if scipy is not None:
+ from scipy import ndimage
+ data = ndimage.gaussian_filter(data, sigma=20)
+ data = numpy.random.poisson(data)
+ data[10] = float("nan")
+ data[50] = float("+inf")
+ data[100] = float("-inf")
+ self.data = data
+ for dialog in self.colorDialogs:
+ dialog.setData(data)
+
+
+def main():
+ app = qt.QApplication([])
+
+ # Create the ad hoc plot widget and change its default colormap
+ example = ColormapDialogExample()
+ example.show()
+
+ app.exec_()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/customDataView.py b/examples/customDataView.py
new file mode 100644
index 0000000..6db5c3e
--- /dev/null
+++ b/examples/customDataView.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+# 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.
+#
+# ###########################################################################*/
+"""Qt data view example
+"""
+
+import logging
+import sys
+
+logging.basicConfig()
+_logger = logging.getLogger("customDataView")
+"""Module logger"""
+
+from silx.gui import qt
+from silx.gui.data.DataViewerFrame import DataViewerFrame
+from silx.gui.data.DataViews import DataView
+from silx.third_party import enum
+
+
+class Color(enum.Enum):
+ RED = 1
+ BLUE = 2
+ GREEN = 3
+
+
+class MyColorView(DataView):
+
+ def __init__(self, parent):
+ DataView.__init__(self, parent)
+
+ def label(self):
+ return "Color"
+
+ def icon(self):
+ pixmap = qt.QPixmap(2, 2)
+ painter = qt.QPainter(pixmap)
+ painter.setPen(qt.QColor(255, 0, 0))
+ painter.drawPoint(qt.QPoint(0, 0))
+ painter.setPen(qt.QColor(255, 255, 0))
+ painter.drawPoint(qt.QPoint(1, 0))
+ painter.setPen(qt.QColor(0, 255, 0))
+ painter.drawPoint(qt.QPoint(0, 1))
+ painter.setPen(qt.QColor(0, 255, 255))
+ painter.drawPoint(qt.QPoint(1, 1))
+ painter.end()
+ pixmap = pixmap.scaled(32, 32, qt.Qt.IgnoreAspectRatio, qt.Qt.FastTransformation)
+ return qt.QIcon(pixmap)
+
+ def setData(self, data):
+ widget = self.getWidget()
+ colors = {Color.RED: "#FF0000",
+ Color.GREEN: "#00FF00",
+ Color.BLUE: "#0000FF"}
+ color = colors.get(data, "#000000")
+ text = "<span style='color:%s'>%s</span>" % (color, str(data))
+ widget.setText(text)
+
+ def axesNames(self, data, info):
+ return None
+
+ def createWidget(self, parent):
+ return qt.QLabel(parent)
+
+ def getDataPriority(self, data, info):
+ if isinstance(data, Color):
+ return 100
+ return self.UNSUPPORTED
+
+
+def main():
+ app = qt.QApplication([])
+
+ widget = DataViewerFrame()
+ widget.addView(MyColorView(widget))
+ widget.setData(Color.GREEN)
+ widget.show()
+ result = app.exec_()
+ # remove ending warnings relative to QTimer
+ app.deleteLater()
+ sys.exit(result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/customHdf5TreeModel.py b/examples/customHdf5TreeModel.py
index 8bd444a..fde76c5 100644
--- a/examples/customHdf5TreeModel.py
+++ b/examples/customHdf5TreeModel.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -101,7 +101,6 @@ _file_cache = {}
def get_hdf5_with_all_types():
- global _file_cache
ID = "alltypes"
if ID in _file_cache:
return _file_cache[ID].name
diff --git a/examples/fftPlotAction.py b/examples/fftPlotAction.py
index 66ecfbd..877225f 100755
--- a/examples/fftPlotAction.py
+++ b/examples/fftPlotAction.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -23,8 +23,8 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script is a simple example of how to create a PlotWindow with a custom
-PlotAction added to the toolbar.
+"""This script is a simple example of how to create a :class:`~silx.gui.plot.PlotWindow`
+with a custom :class:`~silx.gui.plot.actions.PlotAction` added to the toolbar.
The action computes the FFT of all curves and plots their amplitude spectrum.
It also performs the reverse transform.
@@ -179,7 +179,7 @@ y2 = numpy.cos(twopi * 7 * (x - numpy.pi / 3))
# 5 periods of square wave, amplitude 2
y3 = numpy.zeros_like(x)
for i in [0, 2, 4, 6, 8]:
- y3[i * len(x) / 10:(i + 1) * len(x) / 10] = 2
+ y3[i * len(x) // 10:(i + 1) * len(x) // 10] = 2
plotwin.addCurve(x, y1, legend="sin")
plotwin.addCurve(x, y2, legend="cos")
diff --git a/examples/fileDialog.py b/examples/fileDialog.py
new file mode 100644
index 0000000..9730b9a
--- /dev/null
+++ b/examples/fileDialog.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+# 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.
+#
+# ###########################################################################*/
+"""
+Example for the use of the ImageFileDialog.
+"""
+
+from __future__ import absolute_import
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "14/02/2018"
+
+import logging
+from silx.gui import qt
+from silx.gui.dialog.ImageFileDialog import ImageFileDialog
+from silx.gui.dialog.DataFileDialog import DataFileDialog
+import silx.io
+from silx.third_party import enum
+
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+class Mode(enum.Enum):
+ DEFAULT_FILEDIALOG = 0
+ IMAGEFILEDIALOG = 1
+ DATAFILEDIALOG = 2
+ DATAFILEDIALOG_DATASET = 3
+ DATAFILEDIALOG_GROUP = 4
+ DATAFILEDIALOG_NXENTRY = 5
+
+
+class DialogExample(qt.QMainWindow):
+
+ def __init__(self, parent=None):
+ super(DialogExample, self).__init__(parent)
+
+ self.__state = {}
+
+ centralWidget = qt.QWidget(self)
+ layout = qt.QHBoxLayout()
+ centralWidget.setLayout(layout)
+
+ options = self.createOptions()
+ layout.addWidget(options)
+
+ buttonGroup = qt.QGroupBox()
+ buttonGroup.setTitle("Create dialog")
+ layout.addWidget(buttonGroup)
+ buttonLayout = qt.QVBoxLayout()
+ buttonGroup.setLayout(buttonLayout)
+
+ # ImageFileDialog
+
+ b1 = qt.QPushButton(self)
+ b1.setMinimumHeight(50)
+ b1.setText("Open a dialog")
+ b1.clicked.connect(self.openDialog)
+ buttonLayout.addWidget(b1)
+
+ b2 = qt.QPushButton(self)
+ b2.setMinimumHeight(50)
+ b2.setText("Open a dialog with state stored")
+ b2.clicked.connect(self.openDialogStoredState)
+ buttonLayout.addWidget(b2)
+
+ b3 = qt.QPushButton(self)
+ b3.setMinimumHeight(50)
+ b3.setText("Open a dialog at home")
+ b3.clicked.connect(self.openDialogAtHome)
+ buttonLayout.addWidget(b3)
+
+ b4 = qt.QPushButton(self)
+ b4.setMinimumHeight(50)
+ b4.setText("Open a dialog at computer root")
+ b4.clicked.connect(self.openDialogAtComputer)
+ buttonLayout.addWidget(b4)
+
+ self.setCentralWidget(centralWidget)
+
+ def createOptions(self):
+ panel = qt.QGroupBox()
+ panel.setTitle("Options")
+ layout = qt.QVBoxLayout()
+ panel.setLayout(layout)
+ group = qt.QButtonGroup(panel)
+
+ radio = qt.QRadioButton(panel)
+ radio.setText("Qt QFileDialog")
+ radio.setProperty("Mode", Mode.DEFAULT_FILEDIALOG)
+ group.addButton(radio)
+ layout.addWidget(radio)
+
+ radio = qt.QRadioButton(panel)
+ radio.setText("silx ImageFileDialog")
+ radio.setProperty("Mode", Mode.IMAGEFILEDIALOG)
+ group.addButton(radio)
+ layout.addWidget(radio)
+
+ radio = qt.QRadioButton(panel)
+ radio.setChecked(True)
+ radio.setText("silx DataFileDialog")
+ radio.setProperty("Mode", Mode.DATAFILEDIALOG)
+ group.addButton(radio)
+ layout.addWidget(radio)
+
+ radio = qt.QRadioButton(panel)
+ radio.setText("silx DataFileDialog (filter=dataset)")
+ radio.setProperty("Mode", Mode.DATAFILEDIALOG_DATASET)
+ group.addButton(radio)
+ layout.addWidget(radio)
+
+ radio = qt.QRadioButton(panel)
+ radio.setText("silx DataFileDialog (filter=group)")
+ radio.setProperty("Mode", Mode.DATAFILEDIALOG_GROUP)
+ group.addButton(radio)
+ layout.addWidget(radio)
+
+ radio = qt.QRadioButton(panel)
+ radio.setText("silx DataFileDialog (filter=NXentry)")
+ radio.setProperty("Mode", Mode.DATAFILEDIALOG_NXENTRY)
+ group.addButton(radio)
+ layout.addWidget(radio)
+
+ self.__options = group
+ return panel
+
+ def printResult(self, dialog, result):
+ if not result:
+ print("Nothing selected")
+ return
+
+ print("Selection:")
+ if isinstance(dialog, qt.QFileDialog):
+ print("- Files: %s" % dialog.selectedFiles())
+ elif isinstance(dialog, ImageFileDialog):
+ print("- File: %s" % dialog.selectedFile())
+ print("- URL: %s" % dialog.selectedUrl())
+ print("- Data URL: %s" % dialog.selectedDataUrl())
+ image = dialog.selectedImage()
+ print("- Image: <dtype: %s, shape: %s>" % (image.dtype, image.shape))
+ elif isinstance(dialog, DataFileDialog):
+ print("- File: %s" % dialog.selectedFile())
+ print("- URL: %s" % dialog.selectedUrl())
+ print("- Data URL: %s" % dialog.selectedDataUrl())
+ try:
+ data = dialog.selectedData()
+ print("- Data: <dtype: %s, shape: %s>" % (data.dtype, data.shape))
+ except Exception as e:
+ print("- Data: %s" % e)
+
+ url = dialog.selectedDataUrl()
+ with silx.io.open(url.file_path()) as h5:
+ node = h5[url.data_path()]
+ print("- Node: %s" % node)
+ else:
+ assert(False)
+
+ def createDialog(self):
+ print("")
+ print("-------------------------")
+ print("----- Create dialog -----")
+ print("-------------------------")
+ button = self.__options.checkedButton()
+ mode = button.property("Mode")
+ if mode == Mode.DEFAULT_FILEDIALOG:
+ dialog = qt.QFileDialog(self)
+ dialog.setAcceptMode(qt.QFileDialog.AcceptOpen)
+ elif mode == Mode.IMAGEFILEDIALOG:
+ dialog = ImageFileDialog(self)
+ elif mode == Mode.DATAFILEDIALOG:
+ dialog = DataFileDialog(self)
+ elif mode == Mode.DATAFILEDIALOG_DATASET:
+ dialog = DataFileDialog(self)
+ dialog.setFilterMode(DataFileDialog.FilterMode.ExistingDataset)
+ elif mode == Mode.DATAFILEDIALOG_GROUP:
+ dialog = DataFileDialog(self)
+ dialog.setFilterMode(DataFileDialog.FilterMode.ExistingGroup)
+ elif mode == Mode.DATAFILEDIALOG_NXENTRY:
+ def customFilter(obj):
+ if "NX_class" in obj.attrs:
+ return obj.attrs["NX_class"] in [b"NXentry", u"NXentry"]
+ return False
+ dialog = DataFileDialog(self)
+ dialog.setFilterMode(DataFileDialog.FilterMode.ExistingGroup)
+ dialog.setFilterCallback(customFilter)
+ else:
+ assert(False)
+ return dialog
+
+ def openDialog(self):
+ # Clear the dialog
+ dialog = self.createDialog()
+
+ # Execute the dialog as modal
+ result = dialog.exec_()
+ self.printResult(dialog, result)
+
+ def openDialogStoredState(self):
+ # Clear the dialog
+ dialog = self.createDialog()
+ if dialog.__class__ in self.__state:
+ dialog.restoreState(self.__state[dialog.__class__])
+
+ # Execute the dialog as modal
+ result = dialog.exec_()
+ self.__state[dialog.__class__] = dialog.saveState()
+ self.printResult(dialog, result)
+
+ def openDialogAtHome(self):
+ # Clear the dialog
+ path = qt.QDir.homePath()
+ dialog = self.createDialog()
+ dialog.setDirectory(path)
+
+ # Execute the dialog as modal
+ result = dialog.exec_()
+ self.printResult(dialog, result)
+
+ def openDialogAtComputer(self):
+ # Clear the dialog
+ path = ""
+ dialog = self.createDialog()
+ dialog.setDirectory(path)
+
+ # Execute the dialog as modal
+ result = dialog.exec_()
+ self.printResult(dialog, result)
+
+
+def main():
+ app = qt.QApplication([])
+ example = DialogExample()
+ example.show()
+ app.exec_()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/hdf5widget.py b/examples/hdf5widget.py
index 95607ff..7a8e7ae 100755
--- a/examples/hdf5widget.py
+++ b/examples/hdf5widget.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -50,6 +50,7 @@ import h5py
import silx.gui.hdf5
import silx.utils.html
+from silx.third_party import six
from silx.gui import qt
from silx.gui.data.DataViewerFrame import DataViewerFrame
from silx.gui.widgets.ThreadPoolPushButton import ThreadPoolPushButton
@@ -62,8 +63,13 @@ except ImportError:
_file_cache = {}
+def str_attrs(str_list):
+ """Return a numpy array of unicode strings"""
+ text_dtype = h5py.special_dtype(vlen=six.text_type)
+ return numpy.array(str_list, dtype=text_dtype)
+
+
def get_hdf5_with_all_types():
- global _file_cache
ID = "alltypes"
if ID in _file_cache:
return _file_cache[ID].name
@@ -105,7 +111,6 @@ def get_hdf5_with_all_types():
def get_hdf5_with_all_links():
- global _file_cache
ID = "alllinks"
if ID in _file_cache:
return _file_cache[ID].name
@@ -138,7 +143,6 @@ def get_hdf5_with_all_links():
def get_hdf5_with_1000_datasets():
- global _file_cache
ID = "dataset1000"
if ID in _file_cache:
return _file_cache[ID].name
@@ -157,7 +161,6 @@ def get_hdf5_with_1000_datasets():
def get_hdf5_with_10000_datasets():
- global _file_cache
ID = "dataset10000"
if ID in _file_cache:
return _file_cache[ID].name
@@ -176,7 +179,6 @@ def get_hdf5_with_10000_datasets():
def get_hdf5_with_100000_datasets():
- global _file_cache
ID = "dataset100000"
if ID in _file_cache:
return _file_cache[ID].name
@@ -195,7 +197,6 @@ def get_hdf5_with_100000_datasets():
def get_hdf5_with_recursive_links():
- global _file_cache
ID = "recursive_links"
if ID in _file_cache:
return _file_cache[ID].name
@@ -222,7 +223,6 @@ def get_hdf5_with_recursive_links():
def get_hdf5_with_external_recursive_links():
- global _file_cache
ID = "external_recursive_links"
if ID in _file_cache:
return _file_cache[ID][0].name
@@ -254,7 +254,6 @@ def get_hdf5_with_external_recursive_links():
def get_hdf5_with_nxdata():
- global _file_cache
ID = "nxdata"
if ID in _file_cache:
return _file_cache[ID].name
@@ -267,50 +266,55 @@ def get_hdf5_with_nxdata():
g0d = h5.create_group("scalars")
g0d0 = g0d.create_group("0D_scalar")
- g0d0.attrs["NX_class"] = "NXdata"
- g0d0.attrs["signal"] = "scalar"
+ g0d0.attrs["NX_class"] = u"NXdata"
+ g0d0.attrs["signal"] = u"scalar"
g0d0.create_dataset("scalar", data=10)
g0d1 = g0d.create_group("2D_scalars")
- g0d1.attrs["NX_class"] = "NXdata"
- g0d1.attrs["signal"] = "scalars"
+ g0d1.attrs["NX_class"] = u"NXdata"
+ g0d1.attrs["signal"] = u"scalars"
ds = g0d1.create_dataset("scalars", data=numpy.arange(3*10).reshape((3, 10)))
- ds.attrs["interpretation"] = "scalar"
+ ds.attrs["interpretation"] = u"scalar"
g0d1 = g0d.create_group("4D_scalars")
- g0d1.attrs["NX_class"] = "NXdata"
- g0d1.attrs["signal"] = "scalars"
+ g0d1.attrs["NX_class"] = u"NXdata"
+ g0d1.attrs["signal"] = u"scalars"
ds = g0d1.create_dataset("scalars", data=numpy.arange(2*2*3*10).reshape((2, 2, 3, 10)))
- ds.attrs["interpretation"] = "scalar"
+ ds.attrs["interpretation"] = u"scalar"
# SPECTRA
g1d = h5.create_group("spectra")
g1d0 = g1d.create_group("1D_spectrum")
- g1d0.attrs["NX_class"] = "NXdata"
- g1d0.attrs["signal"] = "count"
- g1d0.attrs["axes"] = "energy_calib"
- g1d0.attrs["uncertainties"] = b"energy_errors",
+ g1d0.attrs["NX_class"] = u"NXdata"
+ g1d0.attrs["signal"] = u"count"
+ g1d0.attrs["auxiliary_signals"] = str_attrs(["count.5", "count2"])
+ g1d0.attrs["axes"] = u"energy_calib"
+ g1d0.attrs["uncertainties"] = str_attrs(["energy_errors"])
g1d0.create_dataset("count", data=numpy.arange(10))
+ g1d0.create_dataset("count.5", data=.5*numpy.arange(10))
+ d2 = g1d0.create_dataset("count2", data=2*numpy.arange(10))
+ d2.attrs["long_name"] = u"count multiplied by 2"
g1d0.create_dataset("energy_calib", data=(10, 5)) # 10 * idx + 5
g1d0.create_dataset("energy_errors", data=3.14*numpy.random.rand(10))
+ g1d0.create_dataset("title", data="Title example provided as dataset")
g1d1 = g1d.create_group("2D_spectra")
- g1d1.attrs["NX_class"] = "NXdata"
- g1d1.attrs["signal"] = "counts"
+ g1d1.attrs["NX_class"] = u"NXdata"
+ g1d1.attrs["signal"] = u"counts"
ds = g1d1.create_dataset("counts", data=numpy.arange(3*10).reshape((3, 10)))
- ds.attrs["interpretation"] = "spectrum"
+ ds.attrs["interpretation"] = u"spectrum"
g1d2 = g1d.create_group("4D_spectra")
- g1d2.attrs["NX_class"] = "NXdata"
- g1d2.attrs["signal"] = "counts"
- g1d2.attrs["axes"] = b"energy",
+ g1d2.attrs["NX_class"] = u"NXdata"
+ g1d2.attrs["signal"] = u"counts"
+ g1d2.attrs["axes"] = str_attrs(["energy"])
ds = g1d2.create_dataset("counts", data=numpy.arange(2*2*3*10).reshape((2, 2, 3, 10)))
- ds.attrs["interpretation"] = "spectrum"
- ds = g1d2.create_dataset("errors", data=4.5*numpy.random.rand(2, 2, 3, 10))
+ ds.attrs["interpretation"] = u"spectrum"
+ g1d2.create_dataset("errors", data=4.5*numpy.random.rand(2, 2, 3, 10))
ds = g1d2.create_dataset("energy", data=5+10*numpy.arange(15),
shuffle=True, compression="gzip")
- ds.attrs["long_name"] = "Calibrated energy"
+ ds.attrs["long_name"] = u"Calibrated energy"
ds.attrs["first_good"] = 3
ds.attrs["last_good"] = 12
g1d2.create_dataset("energy_errors", data=10*numpy.random.rand(15))
@@ -319,34 +323,54 @@ def get_hdf5_with_nxdata():
g2d = h5.create_group("images")
g2d0 = g2d.create_group("2D_regular_image")
- g2d0.attrs["NX_class"] = "NXdata"
- g2d0.attrs["signal"] = "image"
- g2d0.attrs["axes"] = b"rows_calib", b"columns_coordinates"
+ g2d0.attrs["NX_class"] = u"NXdata"
+ g2d0.attrs["signal"] = u"image"
+ g2d0.attrs["auxiliary_signals"] = str_attrs(["image2", "image3"])
+ g2d0.attrs["axes"] = str_attrs(["rows_calib", "columns_coordinates"])
+ g2d0.attrs["title"] = u"Title example provided as group attr"
g2d0.create_dataset("image", data=numpy.arange(4*6).reshape((4, 6)))
+ g2d0.create_dataset("image2", data=1/(1.+numpy.arange(4*6).reshape((4, 6))))
+ ds = g2d0.create_dataset("image3", data=-numpy.arange(4*6).reshape((4, 6)))
+ ds.attrs["long_name"] = u"3rd image (2nd auxiliary)"
ds = g2d0.create_dataset("rows_calib", data=(10, 5))
- ds.attrs["long_name"] = "Calibrated Y"
+ ds.attrs["long_name"] = u"Calibrated Y"
g2d0.create_dataset("columns_coordinates", data=0.5+0.02*numpy.arange(6))
+ g2d4 = g2d.create_group("RGBA_image")
+ g2d4.attrs["NX_class"] = u"NXdata"
+ g2d4.attrs["signal"] = u"image"
+ g2d4.attrs["auxiliary_signals"] = u"squared image"
+ g2d4.attrs["axes"] = str_attrs(["rows_calib", "columns_coordinates"])
+ rgba_image = numpy.linspace(0, 1, num=7*8*3).reshape((7, 8, 3))
+ rgba_image[:, :, 1] = 1 - rgba_image[:, :, 1] # invert G channel to add some color
+ ds = g2d4.create_dataset("image", data=rgba_image)
+ ds.attrs["interpretation"] = u"rgba-image"
+ ds = g2d4.create_dataset("squared image", data=rgba_image**2)
+ ds.attrs["interpretation"] = u"rgba-image"
+ ds = g2d4.create_dataset("rows_calib", data=(10, 5))
+ ds.attrs["long_name"] = u"Calibrated Y"
+ g2d4.create_dataset("columns_coordinates", data=0.5+0.02*numpy.arange(8))
+
g2d1 = g2d.create_group("2D_irregular_data")
- g2d1.attrs["NX_class"] = "NXdata"
- g2d1.attrs["signal"] = "data"
- g2d1.attrs["axes"] = b"rows_coordinates", b"columns_coordinates"
+ g2d1.attrs["NX_class"] = u"NXdata"
+ g2d1.attrs["signal"] = u"data"
+ g2d1.attrs["axes"] = str_attrs(["rows_coordinates", "columns_coordinates"])
g2d1.create_dataset("data", data=numpy.arange(64*128).reshape((64, 128)))
g2d1.create_dataset("rows_coordinates", data=numpy.arange(64) + numpy.random.rand(64))
g2d1.create_dataset("columns_coordinates", data=numpy.arange(128) + 2.5 * numpy.random.rand(128))
g2d2 = g2d.create_group("3D_images")
- g2d2.attrs["NX_class"] = "NXdata"
- g2d2.attrs["signal"] = "images"
+ g2d2.attrs["NX_class"] = u"NXdata"
+ g2d2.attrs["signal"] = u"images"
ds = g2d2.create_dataset("images", data=numpy.arange(2*4*6).reshape((2, 4, 6)))
- ds.attrs["interpretation"] = "image"
+ ds.attrs["interpretation"] = u"image"
g2d3 = g2d.create_group("5D_images")
- g2d3.attrs["NX_class"] = "NXdata"
- g2d3.attrs["signal"] = "images"
- g2d3.attrs["axes"] = b"rows_coordinates", b"columns_coordinates"
+ g2d3.attrs["NX_class"] = u"NXdata"
+ g2d3.attrs["signal"] = u"images"
+ g2d3.attrs["axes"] = str_attrs(["rows_coordinates", "columns_coordinates"])
ds = g2d3.create_dataset("images", data=numpy.arange(2*2*2*4*6).reshape((2, 2, 2, 4, 6)))
- ds.attrs["interpretation"] = "image"
+ ds.attrs["interpretation"] = u"image"
g2d3.create_dataset("rows_coordinates", data=5+10*numpy.arange(4))
g2d3.create_dataset("columns_coordinates", data=0.5+0.02*numpy.arange(6))
@@ -354,42 +378,65 @@ def get_hdf5_with_nxdata():
g = h5.create_group("scatters")
gd0 = g.create_group("x_y_scatter")
- gd0.attrs["NX_class"] = "NXdata"
- gd0.attrs["signal"] = "y"
- gd0.attrs["axes"] = b"x",
+ gd0.attrs["NX_class"] = u"NXdata"
+ gd0.attrs["signal"] = u"y"
+ gd0.attrs["axes"] = str_attrs(["x"])
+ gd0.attrs["title"] = u"simple y = f(x) scatters cannot be distinguished from curves"
gd0.create_dataset("y", data=numpy.random.rand(128) - 0.5)
gd0.create_dataset("x", data=2*numpy.random.rand(128))
gd0.create_dataset("x_errors", data=0.05*numpy.random.rand(128))
gd0.create_dataset("errors", data=0.05*numpy.random.rand(128))
gd1 = g.create_group("x_y_value_scatter")
- gd1.attrs["NX_class"] = "NXdata"
- gd1.attrs["signal"] = "values"
- gd1.attrs["axes"] = b"x", b"y"
+ gd1.attrs["NX_class"] = u"NXdata"
+ gd1.attrs["signal"] = u"values"
+ gd1.attrs["auxiliary_signals"] = str_attrs(["values.5", "values2"])
+ gd1.attrs["axes"] = str_attrs(["x", "y"])
+ gd1.attrs["title"] = u"x, y, values scatter with asymmetric y_errors"
gd1.create_dataset("values", data=3.14*numpy.random.rand(128))
+ gd1.create_dataset("values.5", data=0.5*3.14*numpy.random.rand(128))
+ gd1.create_dataset("values2", data=2.*3.14*numpy.random.rand(128))
gd1.create_dataset("y", data=numpy.random.rand(128))
- gd1.create_dataset("y_errors", data=0.02*numpy.random.rand(128))
- gd1.create_dataset("x", data=numpy.random.rand(128))
+ y_errors = [0.03*numpy.random.rand(128), 0.04*numpy.random.rand(128)]
+ gd1.create_dataset("y_errors", data=y_errors)
+ ds = gd1.create_dataset("x", data=2*numpy.random.rand(128))
+ ds.attrs["long_name"] = u"horizontal axis"
gd1.create_dataset("x_errors", data=0.02*numpy.random.rand(128))
# NDIM > 3
g = h5.create_group("cubes")
gd0 = g.create_group("3D_cube")
- gd0.attrs["NX_class"] = "NXdata"
- gd0.attrs["signal"] = "cube"
- gd0.attrs["axes"] = b"img_idx", b"rows_coordinates", b"cols_coordinates"
+ gd0.attrs["NX_class"] = u"NXdata"
+ gd0.attrs["signal"] = u"cube"
+ gd0.attrs["axes"] = str_attrs(["img_idx", "rows_coordinates", "cols_coordinates"])
gd0.create_dataset("cube", data=numpy.arange(4*5*6).reshape((4, 5, 6)))
gd0.create_dataset("img_idx", data=numpy.arange(4))
gd0.create_dataset("rows_coordinates", data=0.1*numpy.arange(5))
gd0.create_dataset("cols_coordinates", data=[0.2, 0.3]) # linear calibration
gd1 = g.create_group("5D")
- gd1.attrs["NX_class"] = "NXdata"
- gd1.attrs["signal"] = "hypercube"
+ gd1.attrs["NX_class"] = u"NXdata"
+ gd1.attrs["signal"] = u"hypercube"
gd1.create_dataset("hypercube",
data=numpy.arange(2*3*4*5*6).reshape((2, 3, 4, 5, 6)))
+ # invalid NXdata
+ g = h5.create_group("invalid")
+ g0 = g.create_group("invalid NXdata")
+ g0.attrs["NX_class"] = u"NXdata"
+
+ g1 = g.create_group("invalid NXentry")
+ g1.attrs["NX_class"] = u"NXentry"
+ g1.attrs["default"] = u"missing NXdata group"
+
+ g2 = g.create_group("invalid NXroot")
+ g2.attrs["NX_class"] = u"NXroot"
+ g2.attrs["default"] = u"invalid NXentry in NXroot"
+ g20 = g2.create_group("invalid NXentry in NXroot")
+ g20.attrs["NX_class"] = u"NXentry"
+ g20.attrs["default"] = u"missing NXdata group"
+
h5.close()
_file_cache[ID] = tmp
@@ -397,7 +444,6 @@ def get_hdf5_with_nxdata():
def get_edf_with_all_types():
- global _file_cache
ID = "alltypesedf"
if ID in _file_cache:
return _file_cache[ID].name
@@ -422,7 +468,6 @@ def get_edf_with_all_types():
def get_edf_with_100000_frames():
- global _file_cache
ID = "frame100000"
if ID in _file_cache:
return _file_cache[ID].name
@@ -586,7 +631,7 @@ class Hdf5TreeViewExample(qt.QMainWindow):
hasDataset = True
break
- if len(menu.children()):
+ if not menu.isEmpty():
menu.addSeparator()
if hasDataset:
@@ -602,7 +647,7 @@ class Hdf5TreeViewExample(qt.QMainWindow):
selectedObjects = event.source().selectedH5Nodes()
menu = event.menu()
- if len(menu.children()):
+ if not menu.isEmpty():
menu.addSeparator()
for obj in selectedObjects:
diff --git a/examples/icons.py b/examples/icons.py
index a6f0ada..673ca6f 100644
--- a/examples/icons.py
+++ b/examples/icons.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -24,12 +24,52 @@
#
# ###########################################################################*/
"""
-Display available project icons using Qt.
+Display icons available in silx.
"""
+
import functools
+import os.path
from silx.gui import qt
import silx.gui.icons
+import silx.resources
+
+
+class AnimatedToolButton(qt.QToolButton):
+ """ToolButton which support animated icons"""
+
+ def __init__(self, parent=None):
+ super(AnimatedToolButton, self).__init__(parent)
+ self.__animatedIcon = None
+
+ def setIcon(self, icon):
+ if isinstance(icon, silx.gui.icons.AbstractAnimatedIcon):
+ self._setAnimatedIcon(icon)
+ else:
+ self._setAnimatedIcon(None)
+ super(AnimatedToolButton, self).setIcon(icon)
+
+ def _setAnimatedIcon(self, icon):
+ if self.__animatedIcon is not None:
+ self.__animatedIcon.unregister(self)
+ self.__animatedIcon.iconChanged.disconnect(self.__updateIcon)
+ self.__animatedIcon = icon
+ if self.__animatedIcon is not None:
+ self.__animatedIcon.register(self)
+ self.__animatedIcon.iconChanged.connect(self.__updateIcon)
+ i = self.__animatedIcon.currentIcon()
+ else:
+ i = qt.QIcon()
+ super(AnimatedToolButton, self).setIcon(i)
+
+ def __updateIcon(self, icon):
+ super(AnimatedToolButton, self).setIcon(icon)
+
+ def icon(self):
+ if self.__animatedIcon is not None:
+ return self.__animatedIcon
+ else:
+ return super(AnimatedToolButton, self).icon()
class IconPreview(qt.QMainWindow):
@@ -66,6 +106,28 @@ class IconPreview(qt.QMainWindow):
button.setChecked(True)
return panel
+ def getAllAvailableIcons(self):
+ def isAnIcon(name):
+ if silx.resources.is_dir("gui/icons/" + name):
+ return False
+ _, ext = os.path.splitext(name)
+ return ext in [".svg", ".png"]
+ icons = silx.resources.list_dir("gui/icons")
+ # filter out sub-directories
+ icons = filter(isAnIcon, icons)
+ # remove extension
+ icons = [i.split(".")[0] for i in icons]
+ # remove duplicated names
+ icons = set(icons)
+ # sort by names
+ return icons
+
+ def getAllAvailableAnimatedIcons(self):
+ icons = silx.resources.list_dir("gui/icons")
+ icons = filter(lambda x: silx.resources.exists("gui/icons/%s/00.png" % x), icons)
+ icons = filter(lambda x: not silx.resources.is_dir("gui/icons/%s/00.png" % x), icons)
+ return icons
+
def createIconPanel(self, parent):
panel = qt.QWidget(parent)
layout = qt.QGridLayout()
@@ -74,25 +136,33 @@ class IconPreview(qt.QMainWindow):
self.tools = []
- import silx.resources
-
- icons = silx.resources.list_dir("gui/icons")
- # filter out sub-directories
- icons = filter(lambda x: not silx.resources.is_dir("gui/icons/" + x), icons)
- # remove extension
- icons = [i.split(".")[0] for i in icons]
- # remove duplicated names
- icons = set(icons)
- # sort by names
+ # Sort together animated and non animated icons
+ fix_icons = self.getAllAvailableIcons()
+ animated_icons = self.getAllAvailableAnimatedIcons()
+ icons = []
+ icons.extend([(i, "_") for i in fix_icons])
+ icons.extend([(i, "anim") for i in animated_icons])
icons = sorted(icons)
- for i, icon_name in enumerate(icons):
+ for i, icon_info in enumerate(icons):
+ icon_name, icon_kind = icon_info
col, line = i / 10, i % 10
- icon = silx.gui.icons.getQIcon(icon_name)
- tool = qt.QToolButton(panel)
+ if icon_kind == "anim":
+ tool = AnimatedToolButton(panel)
+ try:
+ icon = silx.gui.icons.getAnimatedIcon(icon_name)
+ except ValueError:
+ icon = qt.QIcon()
+ tool.setToolTip("Animated icon '%s'" % icon_name)
+ else:
+ tool = qt.QToolButton(panel)
+ try:
+ icon = silx.gui.icons.getQIcon(icon_name)
+ except ValueError:
+ icon = qt.QIcon()
+ tool.setToolTip("Icon '%s'" % icon_name)
tool.setIcon(icon)
tool.setIconSize(qt.QSize(32, 32))
- tool.setToolTip(icon_name)
layout.addWidget(tool, col, line)
self.tools.append(tool)
diff --git a/examples/imageview.py b/examples/imageview.py
index 34595e2..c4f80bd 100755
--- a/examples/imageview.py
+++ b/examples/imageview.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -24,8 +24,9 @@
#
# ###########################################################################*/
"""
-Example to show the use of `ImageView` widget. It can be used to open an EDF
-or TIFF file from the shell command line.
+Example to show the use of :class:`~silx.gui.plot.ImageView.ImageView` widget.
+
+It can be used to open an EDF or TIFF file from the shell command line.
To view an image file with the current installed silx library:
``python examples/imageview.py <file to open>``
diff --git a/examples/plot3dSceneWindow.py b/examples/plot3dSceneWindow.py
new file mode 100644
index 0000000..efffcd3
--- /dev/null
+++ b/examples/plot3dSceneWindow.py
@@ -0,0 +1,197 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2017-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.
+#
+# ###########################################################################*/
+"""
+This script displays the different items of :class:`~silx.gui.plot3d.SceneWindow`.
+
+It shows the different visualizations of :class:`~silx.gui.plot3d.SceneWindow`
+and :class:`~silx.gui.plot3d.SceneWidget`.
+It illustrates the API to set those items.
+
+It features:
+
+- 2D images: data and RGBA images
+- 2D scatter data, displayed either as markers, wireframe or surface.
+- 3D scatter plot
+- 3D scalar field with iso-surface and cutting plane.
+- A clipping plane.
+
+"""
+
+from __future__ import absolute_import
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "17/11/2017"
+
+import numpy
+
+from silx.gui import qt
+from silx.gui.plot3d.SceneWindow import SceneWindow, items
+
+SIZE = 1024
+
+# Create QApplication
+qapp = qt.QApplication([])
+
+# Create a SceneWindow widget
+window = SceneWindow()
+
+# Get the SceneWidget contained in the window and set its colors
+sceneWidget = window.getSceneWidget()
+sceneWidget.setBackgroundColor((0.8, 0.8, 0.8, 1.))
+sceneWidget.setForegroundColor((1., 1., 1., 1.))
+sceneWidget.setTextColor((0.1, 0.1, 0.1, 1.))
+
+
+# 2D Image ###
+
+# Add a dummy RGBA image
+img = numpy.random.random(3 * SIZE ** 2).reshape(SIZE, SIZE, 3) # Dummy image
+
+imageRgba = sceneWidget.addImage(img) # Add ImageRgba item to the scene
+
+# Set imageRgba transform
+imageRgba.setTranslation(SIZE*.15, SIZE*.15, 0.) # Translate the image
+# Rotate the image by 45 degrees around its center
+imageRgba.setRotationCenter('center', 'center', 0.)
+imageRgba.setRotation(45., axis=(0., 0., 1.))
+imageRgba.setScale(0.7, 0.7, 0.7) # Scale down image
+
+
+# Add a data image
+data = numpy.arange(SIZE ** 2).reshape(SIZE, SIZE) # Dummy data
+imageData = sceneWidget.addImage(data) # Add ImageData item to the scene
+
+# Set imageData transform
+imageData.setTranslation(0., SIZE, 0.) # Translate the image
+
+# Set imageData properties
+imageData.setInterpolation('linear') # 'linear' or 'nearest' interpolation
+imageData.getColormap().setName('magma') # Use magma colormap
+
+
+# 2D scatter data ###
+
+# Create 2D scatter dummy data
+x = numpy.random.random(10 ** 3)
+y = numpy.random.random(len(x))
+values = numpy.exp(- 11. * ((x - .5) ** 2 + (y - .5) ** 2))
+
+# Add 2D scatter data with 6 different visualisations
+for row, heightMap in enumerate((False, True)):
+ for col, mode in enumerate(('points', 'lines', 'solid')):
+ # Add a new scatter
+ item = sceneWidget.add2DScatter(x, y, values)
+
+ # Set 2D scatter item tranform
+ item.setTranslation(SIZE + col * SIZE, row * SIZE, 0.)
+ item.setScale(SIZE, SIZE, SIZE)
+
+ # Set 2D scatter item properties
+ item.setHeightMap(heightMap)
+ item.setVisualization(mode)
+ item.getColormap().setName('viridis')
+ item.setLineWidth(2.)
+
+
+# Group ###
+
+# Create a group item and add it to the scene
+# The group children share the group transform
+group = items.GroupItem() # Create a new group item
+group.setTranslation(SIZE * 4, 0., 0.) # Translate the group
+
+
+# Clipping plane ###
+
+# Add a clipping plane to the group (and thus to the scene)
+# This item hides part of other items in the half space defined by the plane.
+# Clipped items are those belonging to the same group (i.e., brothers) that
+# comes after the clipping plane.
+clipPlane = items.ClipPlane() # Create a new clipping plane item
+clipPlane.setNormal((1., -0.35, 0.)) # Set its normal
+clipPlane.setPoint((0., 0., 0.)) # Set a point on the plane
+group.addItem(clipPlane) # Add clipping plane to the group
+
+
+# 3D scatter data ###
+
+# Create dummy data
+x = numpy.random.random(10**3)
+y = numpy.random.random(len(x))
+z = numpy.random.random(len(x))
+values = numpy.random.random(len(x))
+
+# Create a 3D scatter item and set its data
+scatter3d = items.Scatter3D()
+scatter3d.setData(x, y, z, values)
+
+# Set scatter3d transform
+scatter3d.setScale(SIZE, SIZE, SIZE)
+
+# Set scatter3d properties
+scatter3d.getColormap().setName('magma') # Use 'magma' colormap
+scatter3d.setSymbol('d') # Use diamond markers
+scatter3d.setSymbolSize(11) # Set the size of the markers
+
+# Add scatter3d to the group (and thus to the scene)
+group.addItem(scatter3d)
+
+
+# 3D scalar volume ###
+
+# Create dummy 3D array data
+x, y, z = numpy.meshgrid(numpy.linspace(-10, 10, 64),
+ numpy.linspace(-10, 10, 64),
+ numpy.linspace(-10, 10, 64))
+data = numpy.sin(x * y * z) / (x * y * z)
+
+# Create a 3D scalar field item and set its data
+volume = items.ScalarField3D() # Create a new 3D volume item
+volume.setData(data) # Set its data
+group.addItem(volume) # Add it to the group (and thus to the scene)
+
+# Set volume tranform
+volume.setTranslation(0., SIZE, 0.)
+volume.setScale(SIZE/data.shape[2], SIZE/data.shape[1], SIZE/data.shape[0])
+
+# Add isosurfaces to the volume item given isolevel and color
+volume.addIsosurface(0.2, '#FF000080')
+volume.addIsosurface(0.5, '#0000FFFF')
+
+# Set the volume cut plane
+cutPlane = volume.getCutPlanes()[0] # Get the volume's cut plane
+cutPlane.setVisible(True) # Set it to be visible
+cutPlane.getColormap().setName('jet') # Set cut plane's colormap
+cutPlane.setNormal((0., 0., 1.)) # Set cut plane's normal
+cutPlane.moveToCenter() # Place the cut plane at the center of the volume
+
+sceneWidget.addItem(group) # Add the group as an item of the scene
+
+# Show the SceneWidget widget
+window.show()
+
+# Run Qt event loop
+qapp.exec_()
diff --git a/examples/plotClearAction.py b/examples/plotClearAction.py
new file mode 100644
index 0000000..e1130cb
--- /dev/null
+++ b/examples/plotClearAction.py
@@ -0,0 +1,75 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2018 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""This script shows how to create a minimalistic
+:class:`~silx.gui.plot.actions.PlotAction` that clear the plot.
+
+This illustrates how to add more buttons in a plot widget toolbar.
+"""
+
+__authors__ = ["T. VINCENT"]
+__license__ = "MIT"
+__date__ = "14/02/2018"
+
+
+from silx.gui.plot.actions import PlotAction
+
+
+class ClearPlotAction(PlotAction):
+ """A QAction that can be added to PlotWidget toolbar to clear the plot"""
+
+ def __init__(self, plot, parent=None):
+ super(ClearPlotAction, self).__init__(
+ plot,
+ icon='close',
+ text='Clear',
+ tooltip='Clear the plot',
+ triggered=self._clear,
+ parent=parent)
+
+ def _clear(self):
+ """Handle action triggered and clear the plot"""
+ self.plot.clear()
+
+
+if __name__ == '__main__':
+ from silx.gui import qt
+ from silx.gui.plot import Plot1D
+
+ app = qt.QApplication([]) # First create QApplication
+
+ plot = Plot1D() # Create plotting widget
+
+ # Create a toolbar and add it to the plot widget
+ toolbar = qt.QToolBar()
+ plot.addToolBar(toolbar)
+
+ # Create clear action and add it to the toolbar
+ action = ClearPlotAction(plot, parent=plot)
+ toolbar.addAction(action)
+
+ plot.addCurve((0, 1, 2, 3, 4), (0, 1, 1.5, 1, 0)) # Add a curve to the plot
+
+ plot.show() # Show the plot widget
+ app.exec_() # Start Qt application
diff --git a/examples/plotContextMenu.py b/examples/plotContextMenu.py
index 3e9af1e..5f02f5f 100644
--- a/examples/plotContextMenu.py
+++ b/examples/plotContextMenu.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-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
@@ -23,14 +23,17 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script illustrates the addition of a context menu to a PlotWidget.
+"""This script illustrates the addition of a context menu to a
+:class:`~silx.gui.plot.PlotWidget`.
This is done by adding a custom context menu to the plot area of PlotWidget:
- set the context menu policy of the plot area to Qt.CustomContextMenu.
- connect to the plot area customContextMenuRequested signal.
-The same method works with PlotWindow, Plot1D and Plot2D widgets as they
-inherit from PlotWidget.
+The same method works with :class:`~silx.gui.plot.PlotWindow.PlotWindow`,
+:class:`~silx.gui.plot.PlotWindow.Plot1D` and
+:class:`~silx.gui.plot.PlotWindow.Plot2D` widgets as they
+inherit from :class:`~silx.gui.plot.PlotWidget`.
For more information on context menus, see Qt documentation.
"""
diff --git a/examples/plotItemsSelector.py b/examples/plotItemsSelector.py
index 8f29457..458bbeb 100755
--- a/examples/plotItemsSelector.py
+++ b/examples/plotItemsSelector.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-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
@@ -24,7 +24,7 @@
#
# ###########################################################################*/
"""This example illustrates how to use a :class:`ItemsSelectionDialog` widget
-associated with a :class:`PlotWidget`.
+associated with a :class:`~silx.gui.plot.PlotWidget`.
"""
__authors__ = ["P. Knobel"]
diff --git a/examples/plotUpdateFromThread.py b/examples/plotUpdateFromThread.py
index d36bc48..36df63f 100644
--- a/examples/plotUpdateFromThread.py
+++ b/examples/plotUpdateFromThread.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-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
@@ -22,14 +22,14 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script illustrates the update of a silx.gui.plot widget from a thread.
+"""This script illustrates the update of a :mod:`silx.gui.plot` widget from a thread.
The problem is that plot and GUI methods should be called from the main thread.
To safely update the plot from another thread, one need to make the update
asynchronously from the main thread.
In this example, this is achieved through a Qt signal.
-In this example we create a subclass of :class:`silx.gui.plot.Plot1D`
+In this example we create a subclass of :class:`~silx.gui.plot.PlotWindow.Plot1D`
that adds a thread-safe method to add curves:
:meth:`ThreadSafePlot1D.addCurveThreadSafe`.
This thread-safe method is then called from a thread to update the plot.
diff --git a/examples/plotWidget.py b/examples/plotWidget.py
index 698fe56..24de519 100644
--- a/examples/plotWidget.py
+++ b/examples/plotWidget.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-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
@@ -22,15 +22,15 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script shows how to subclass :class:`PlotWidget` to tune its tools.
+"""This script shows how to subclass :class:`~silx.gui.plot.PlotWidget` to tune its tools.
-It subclasses a :class:`silx.gui.plot.PlotWidget` and adds toolbars and
+It subclasses a :class:`~silx.gui.plot.PlotWidget` and adds toolbars and
a colorbar by using pluggable widgets:
- QAction from :mod:`silx.gui.plot.actions`
- QToolButton from :mod:`silx.gui.plot.PlotToolButtons`
- QToolBar from :mod:`silx.gui.plot.PlotTools`
-- :class:`ColorBarWidget` from :mod:`silx.gui.plot.ColorBar`.
+- :class:`silx.gui.plot.ColorBar.ColorBarWidget`
"""
__authors__ = ["T. Vincent"]
diff --git a/examples/printPreview.py b/examples/printPreview.py
index 187ad84..7567adb 100755
--- a/examples/printPreview.py
+++ b/examples/printPreview.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -24,12 +24,13 @@
#
# ###########################################################################*/
"""This script illustrates how to add a print preview tool button to any plot
-widget inheriting :class:`PlotWidget`.
+widget inheriting :class:`~silx.gui.plot.PlotWidget`.
Three plot widgets are instantiated. One of them uses a standalone
-:class:`PrintPreviewToolButton`, while the other two use a
-:class:`SingletonPrintPreviewToolButton` which allows them to send their content
-to the same print preview page.
+:class:`~silx.gui.plot.PrintPreviewToolButton.PrintPreviewToolButton`,
+while the other two use a
+:class:`~silx.gui.plot.PrintPreviewToolButton.SingletonPrintPreviewToolButton`
+which allows them to send their content to the same print preview page.
"""
__authors__ = ["P. Knobel"]
__license__ = "MIT"
diff --git a/examples/shiftPlotAction.py b/examples/shiftPlotAction.py
index 7cac08c..f272cda 100755
--- a/examples/shiftPlotAction.py
+++ b/examples/shiftPlotAction.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -23,8 +23,8 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script is a simple (trivial) example of how to create a PlotWindow,
-create a custom :class:`PlotAction` and add it to the toolbar.
+"""This script is a simple (trivial) example of how to create a :class:`~silx.gui.plot.PlotWindow`,
+create a custom :class:`~silx.gui.plot.actions.PlotAction` and add it to the toolbar.
The action simply shifts the selected curve up by 1 unit by adding 1 to each
value of y.
diff --git a/examples/simplewidget.py b/examples/simplewidget.py
index 605beb3..8bfe7d5 100755
--- a/examples/simplewidget.py
+++ b/examples/simplewidget.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -27,7 +27,8 @@
It shows the following widgets:
-- :class:WaitingPushButton: A button with a progress-like waiting animated icon
+- :class:`~silx.gui.widgets.WaitingPushButton`:
+ A button with a progress-like waiting animated icon.
"""
__authors__ = ["V. Valls"]
diff --git a/examples/stackView.py b/examples/stackView.py
index 0447cea..4737251 100644
--- a/examples/stackView.py
+++ b/examples/stackView.py
@@ -2,7 +2,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+# 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
@@ -23,8 +23,8 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script is a simple example to illustrate how to use the StackView
-widget.
+"""This script is a simple example to illustrate how to use the
+:mod:`~silx.gui.plot.StackView` widget.
"""
import numpy
import sys
diff --git a/examples/syncaxis.py b/examples/syncaxis.py
index 1033738..02505c9 100644
--- a/examples/syncaxis.py
+++ b/examples/syncaxis.py
@@ -97,5 +97,6 @@ class SyncPlot(qt.QMainWindow):
if __name__ == "__main__":
app = qt.QApplication([])
window = SyncPlot()
+ window.setAttribute(qt.Qt.WA_DeleteOnClose, True)
window.setVisible(True)
app.exec_()