summaryrefslogtreecommitdiff
path: root/silx/sx
diff options
context:
space:
mode:
Diffstat (limited to 'silx/sx')
-rw-r--r--silx/sx/__init__.py38
-rw-r--r--silx/sx/_plot.py338
-rw-r--r--silx/sx/_plot3d.py246
3 files changed, 585 insertions, 37 deletions
diff --git a/silx/sx/__init__.py b/silx/sx/__init__.py
index 87bfb9e..bdec6e6 100644
--- a/silx/sx/__init__.py
+++ b/silx/sx/__init__.py
@@ -1,7 +1,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
@@ -22,31 +22,35 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""Convenient module to use main features of silx from the console.
-
-Usage from (I)Python console or notebook:
+"""This is a convenient package to use from Python or IPython interpreter.
+It loads the main features of silx and provides high-level functions.
>>> from silx import sx
-With IPython/jupyter, this also runs %pylab.
-From the console, it sets-up Qt in order to allow using GUI widgets.
+When used in an interpreter is sets-up Qt and loads some silx widgets.
+When used in a `jupyter <https://jupyter.org/>`_ /
+`IPython <https://ipython.org/>`_ notebook, neither Qt nor silx widgets are loaded.
+
+When used in `IPython <https://ipython.org/>`_, it also runs ``%pylab``,
+thus importing `numpy <http://www.numpy.org/>`_ and `matplotlib <https://matplotlib.org/>`_.
"""
+
__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "16/01/2017"
-import logging
+import logging as _logging
import sys as _sys
-_logger = logging.getLogger(__name__)
+_logger = _logging.getLogger(__name__)
# Init logging when used from the console
if hasattr(_sys, 'ps1'):
- logging.basicConfig()
+ _logging.basicConfig()
# Probe ipython
try:
@@ -81,12 +85,22 @@ else:
del _icons # clean-up namespace
from silx.gui.plot import * # noqa
- from ._plot import plot, imshow # noqa
+ from ._plot import plot, imshow, ginput # noqa
+
+ try:
+ import OpenGL as _OpenGL
+ except ImportError:
+ _logger.warning(
+ 'Not loading silx.gui.plot3d features: PyOpenGL is not installed')
+ else:
+ del _OpenGL # clean-up namespace
+ from ._plot3d import contour3d, points3d # noqa
# %pylab
if _get_ipython is not None and _get_ipython() is not None:
- _get_ipython().enable_pylab(gui='inline' if _IS_NOTEBOOK else 'qt')
+ _get_ipython().enable_pylab(gui='inline' if _IS_NOTEBOOK else 'qt',
+ import_all=False)
# Clean-up
@@ -96,7 +110,7 @@ del _IS_NOTEBOOK
# Load some silx stuff in namespace
-from silx import * # noqa
+from silx import version # noqa
from silx.io import open # noqa
from silx.io import * # noqa
from silx.math import Histogramnd, HistogramndLut # noqa
diff --git a/silx/sx/_plot.py b/silx/sx/_plot.py
index e81e57e..dfc24d9 100644
--- a/silx/sx/_plot.py
+++ b/silx/sx/_plot.py
@@ -1,7 +1,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,13 +27,19 @@
__authors__ = ["T. Vincent"]
__license__ = "MIT"
-__date__ = "27/06/2017"
+__date__ = "26/02/2018"
+import collections
import logging
+import time
+import weakref
+
import numpy
-from ..gui.plot import Plot1D, Plot2D
+from ..utils.weakref import WeakList
+from ..gui import qt
+from ..gui.plot import Plot1D, Plot2D, PlotWidget
from ..gui.plot.Colors import COLORDICT
from ..gui.plot.Colormap import Colormap
from silx.third_party import six
@@ -41,19 +47,15 @@ from silx.third_party import six
_logger = logging.getLogger(__name__)
+_plots = WeakList()
+"""List of widgets created through plot and imshow"""
+
def plot(*args, **kwargs):
"""
- Plot curves in a dedicated widget.
-
- This function supports a subset of matplotlib.pyplot.plot arguments.
- See: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot
-
- It opens a silx PlotWindow with its associated tools.
+ Plot curves in a :class:`~silx.gui.plot.PlotWindow.Plot1D` widget.
- Examples:
-
- First import :mod:`sx` function:
+ How to use:
>>> from silx import sx
>>> import numpy
@@ -67,8 +69,7 @@ def plot(*args, **kwargs):
>>> angles = numpy.linspace(0, numpy.pi, 100)
>>> sin_a = numpy.sin(angles)
- >>> plot_sinus = sx.plot(angles, sin_a,
- ... xlabel='angle (radian)', ylabel='sin(a)')
+ >>> plot_sinus = sx.plot(angles, sin_a, xlabel='angle (radian)', ylabel='sin(a)')
Plot many curves by giving a 2D array, provided xn, yn arrays:
@@ -96,13 +97,13 @@ def plot(*args, **kwargs):
- '-.' dash-dot line
- ':' dotted line
- Remark: The first curve will always be displayed in black no matter the
- given color. This is because it is selected by default and this is shown
- by using the black color.
-
If provided, the names arguments color, linestyle, linewidth and marker
override any style provided to a curve.
+ This function supports a subset of `matplotlib.pyplot.plot
+ <http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot>`_
+ arguments.
+
:param str color: Color to use for all curves (default: None)
:param str linestyle: Type of line to use for all curves (default: None)
:param float linewidth: With of all the curves (default: 1)
@@ -194,6 +195,7 @@ def plot(*args, **kwargs):
color=color or curve_color)
plt.show()
+ _plots.insert(0, plt)
return plt
@@ -202,14 +204,10 @@ def imshow(data=None, cmap=None, norm=Colormap.LINEAR,
aspect=False,
origin=(0., 0.), scale=(1., 1.),
title='', xlabel='X', ylabel='Y'):
- """Plot an image in a dedicated widget.
-
- This function supports a subset of matplotlib.pyplot.imshow arguments.
- See: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.imshow
-
- It opens a silx PlotWindow with its associated tools.
+ """
+ Plot an image in a :class:`~silx.gui.plot.PlotWindow.Plot2D` widget.
- Example to plot an image:
+ How to use:
>>> from silx import sx
>>> import numpy
@@ -217,6 +215,10 @@ def imshow(data=None, cmap=None, norm=Colormap.LINEAR,
>>> data = numpy.random.random(1024 * 1024).reshape(1024, 1024)
>>> plt = sx.imshow(data, title='Random data')
+ This function supports a subset of `matplotlib.pyplot.imshow
+ <http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.imshow>`_
+ arguments.
+
:param data: data to plot as an image
:type data: numpy.ndarray-like with 2 dimensions
:param str cmap: The name of the colormap to use for the plot.
@@ -268,4 +270,290 @@ def imshow(data=None, cmap=None, norm=Colormap.LINEAR,
plt.addImage(data, origin=origin, scale=scale)
plt.show()
+ _plots.insert(0, plt)
return plt
+
+
+class _GInputResult(tuple):
+ """Object storing :func:`ginput` result
+
+ :param position: Selected point coordinates in the plot (x, y)
+ :param Item item: Plot item under the selected position
+ :param indices: Selected indices in the data of the item.
+ For a curve it is a list of indices, for an image it is (row, column)
+ :param data: Value of data at selected indices.
+ For a curve it is an array of values, for an image it is a single value
+ """
+
+ def __new__(cls, position, item, indices, data):
+ return super(_GInputResult, cls).__new__(cls, position)
+
+ def __init__(self, position, item, indices, data):
+ self._itemRef = weakref.ref(item) if item is not None else None
+ self._indices = numpy.array(indices, copy=True)
+ if isinstance(data, collections.Iterable):
+ self._data = numpy.array(data, copy=True)
+ else:
+ self._data = data
+
+ def getItem(self):
+ """Returns the item at the selected position if any.
+
+ :return: plot item under the selected postion.
+ It is None if there was no item at that position or if
+ it is no more in the plot.
+ :rtype: silx.gui.plot.items.Item"""
+ return None if self._itemRef is None else self._itemRef()
+
+ def getIndices(self):
+ """Returns indices in data array at the select position
+
+ :return: 1D array of indices for curve and (row, column) for images
+ :rtype: numpy.ndarray
+ """
+ return numpy.array(self._indices, copy=True)
+
+ def getData(self):
+ """Returns data value at the selected position.
+
+ For curves, an array of (x, y) values close to the point is returned.
+ For images, either a single value or a RGB(A) array is returned.
+
+ :return: 2D array of (x, y) data values for curves (Nx2),
+ a single value for data images and RGB(A) array for images.
+ """
+ if isinstance(self._data, numpy.ndarray):
+ return numpy.array(self._data, copy=True)
+ else:
+ return self._data
+
+
+class _GInputHandler(qt.QEventLoop):
+ """Implements :func:`ginput`
+
+ :param PlotWidget plot:
+ :param int n:
+ :param float timeout:
+ """
+
+ def __init__(self, plot, n, timeout):
+ super(_GInputHandler, self).__init__()
+
+ if not isinstance(plot, PlotWidget):
+ raise ValueError('plot is not a PlotWidget: %s', plot)
+
+ self._plot = plot
+ self._timeout = timeout
+ self._markersAndResult = []
+ self._totalPoints = n
+ self._endTime = 0.
+
+ def eventFilter(self, obj, event):
+ """Event filter for plot hide event"""
+ if event.type() == qt.QEvent.Hide:
+ self.quit()
+
+ elif event.type() == qt.QEvent.KeyPress:
+ if event.key() in (qt.Qt.Key_Delete, qt.Qt.Key_Backspace) or (
+ event.key() == qt.Qt.Key_Z and event.modifiers() & qt.Qt.ControlModifier):
+ if len(self._markersAndResult) > 0:
+ legend, _ = self._markersAndResult.pop()
+ self._plot.remove(legend=legend, kind='marker')
+
+ self._updateStatusBar()
+ return True # Stop further handling of those keys
+
+ elif event.key() == qt.Qt.Key_Return:
+ self.quit()
+ return True # Stop further handling of those keys
+
+ return super(_GInputHandler, self).eventFilter(obj, event)
+
+ def exec_(self):
+ """Run blocking ginput handler
+
+ :returns: List of selected points
+ """
+ # Bootstrap
+ self._plot.setInteractiveMode(mode='zoom')
+ self._handleInteractiveModeChanged(None)
+ self._plot.sigInteractiveModeChanged.connect(
+ self._handleInteractiveModeChanged)
+
+ self._plot.installEventFilter(self)
+
+ # Run
+ if self._timeout:
+ timeoutTimer = qt.QTimer()
+ timeoutTimer.timeout.connect(self._updateStatusBar)
+ timeoutTimer.start(1000)
+
+ self._endTime = time.time() + self._timeout
+ self._updateStatusBar()
+
+ returnCode = super(_GInputHandler, self).exec_()
+
+ timeoutTimer.stop()
+ else:
+ returnCode = super(_GInputHandler, self).exec_()
+
+ # Clean-up
+ self._plot.removeEventFilter(self)
+
+ self._plot.sigInteractiveModeChanged.disconnect(
+ self._handleInteractiveModeChanged)
+
+ currentMode = self._plot.getInteractiveMode()
+ if currentMode['mode'] == 'zoom': # Stop handling mouse click
+ self._plot.sigPlotSignal.disconnect(self._handleSelect)
+
+ self._plot.statusBar().clearMessage()
+
+ points = tuple(result for _, result in self._markersAndResult)
+
+ for legend, _ in self._markersAndResult:
+ self._plot.remove(legend=legend, kind='marker')
+ self._markersAndResult = []
+
+ return points if returnCode == 0 else ()
+
+ def _updateStatusBar(self):
+ """Update status bar message"""
+ msg = 'ginput: %d/%d input points' % (len(self._markersAndResult),
+ self._totalPoints)
+ if self._timeout:
+ remaining = self._endTime - time.time()
+ if remaining < 0:
+ self.quit()
+ return
+ msg += ', %d seconds remaining' % max(1, int(remaining))
+
+ currentMode = self._plot.getInteractiveMode()
+ if currentMode['mode'] != 'zoom':
+ msg += ' (Use zoom mode to add/remove points)'
+
+ self._plot.statusBar().showMessage(msg)
+
+ def _handleSelect(self, event):
+ """Handle mouse events"""
+ if event['event'] == 'mouseClicked' and event['button'] == 'left':
+ x, y = event['x'], event['y']
+ xPixel, yPixel = event['xpixel'], event['ypixel']
+
+ # Add marker
+ legend = "sx.ginput %d" % len(self._markersAndResult)
+ self._plot.addMarker(
+ x, y,
+ legend=legend,
+ text='%d' % len(self._markersAndResult),
+ color='red',
+ draggable=False)
+
+ # Pick item at selected position
+ picked = self._plot._pickImageOrCurve(xPixel, yPixel)
+
+ if picked is None:
+ result = _GInputResult((x, y),
+ item=None,
+ indices=numpy.array((), dtype=int),
+ data=None)
+
+ elif picked[0] == 'curve':
+ curve = picked[1]
+ indices = picked[2]
+ xData = curve.getXData(copy=False)[indices]
+ yData = curve.getYData(copy=False)[indices]
+ result = _GInputResult((x, y),
+ item=curve,
+ indices=indices,
+ data=numpy.array((xData, yData)).T)
+
+ elif picked[0] == 'image':
+ image = picked[1]
+ # Get corresponding coordinate in image
+ origin = image.getOrigin()
+ scale = image.getScale()
+ column = int((x - origin[0]) / float(scale[0]))
+ row = int((y - origin[1]) / float(scale[1]))
+ data = image.getData(copy=False)[row, column]
+ result = _GInputResult((x, y),
+ item=image,
+ indices=(row, column),
+ data=data)
+
+ self._markersAndResult.append((legend, result))
+ self._updateStatusBar()
+ if len(self._markersAndResult) == self._totalPoints:
+ self.quit()
+
+ def _handleInteractiveModeChanged(self, source):
+ """Handle change of interactive mode in the plot
+
+ :param source: Objects that triggered the mode change
+ """
+ mode = self._plot.getInteractiveMode()
+ if mode['mode'] == 'zoom': # Handle click events
+ self._plot.sigPlotSignal.connect(self._handleSelect)
+ else: # Do not handle click event
+ self._plot.sigPlotSignal.disconnect(self._handleSelect)
+ self._updateStatusBar()
+
+
+def ginput(n=1, timeout=30, plot=None):
+ """Get input points on a plot.
+
+ If no plot is provided, it uses a plot widget created with
+ either :func:`silx.sx.plot` or :func:`silx.sx.imshow`.
+
+ How to use:
+
+ >>> from silx import sx
+
+ >>> sx.imshow(image) # Plot the image
+ >>> sx.ginput(1) # Request selection on the image plot
+ ((0.598, 1.234))
+
+ How to get more information about the selected positions:
+
+ >>> positions = sx.ginput(1)
+
+ >>> positions[0].getData() # Returns value(s) at selected position
+
+ >>> positions[0].getIndices() # Returns data indices at selected position
+
+ >>> positions[0].getItem() # Returns plot item at selected position
+
+ :param int n: Number of points the user need to select
+ :param float timeout: Timeout in seconds before ginput returns
+ event if selection is not completed
+ :param silx.gui.plot.PlotWidget.PlotWidget plot: An optional PlotWidget
+ from which to get input
+ :return: List of clicked points coordinates (x, y) in plot
+ :raise ValueError: If provided plot is not a PlotWidget
+ """
+ if plot is None:
+ # Select most recent visible plot widget
+ for widget in _plots:
+ if widget.isVisible():
+ plot = widget
+ break
+ else: # If no plot widgets are visible, take most recent one
+ try:
+ plot = _plots[0]
+ except IndexError:
+ pass
+ else:
+ plot.show()
+
+ if plot is None:
+ _logger.warning('No plot available to perform ginput, create one')
+ plot = Plot1D()
+ plot.show()
+
+ plot.raise_() # So window becomes the top level one
+
+ _logger.info('Performing ginput with plot widget %s', str(plot))
+ handler = _GInputHandler(plot, n, timeout)
+ points = handler.exec_()
+
+ return points
diff --git a/silx/sx/_plot3d.py b/silx/sx/_plot3d.py
new file mode 100644
index 0000000..3e67fe0
--- /dev/null
+++ b/silx/sx/_plot3d.py
@@ -0,0 +1,246 @@
+# 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 module adds convenient functions to use plot3d widgets from the console.
+"""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "07/02/2018"
+
+
+from collections import Iterable
+import logging
+import numpy
+
+from ..gui import qt
+from ..gui.plot3d.SceneWindow import SceneWindow
+from ..gui.plot3d.ScalarFieldView import ScalarFieldView
+from ..gui.plot3d import SFViewParamTree
+from ..gui.plot.Colormap import Colormap
+from ..gui.plot.Colors import rgba
+
+
+_logger = logging.getLogger(__name__)
+
+
+def contour3d(scalars,
+ contours=1,
+ copy=True,
+ color=None,
+ colormap='viridis',
+ vmin=None,
+ vmax=None,
+ opacity=1.):
+ """
+ Plot isosurfaces of a 3D scalar field in a :class:`~silx.gui.plot3d.ScalarFieldView.ScalarFieldView` widget.
+
+ How to use:
+
+ >>> from silx import sx
+
+ Provided data, a 3D scalar field as a numpy array of float32:
+
+ >>> plot3d_window = sx.contour3d(data)
+
+ Alternatively you can provide the level of the isosurfaces:
+
+ >>> plot3d_window = sx.contour3d(data, contours=[0.2, 0.4])
+
+ This function provides a subset of `mayavi.mlab.contour3d
+ <http://docs.enthought.com/mayavi/mayavi/auto/mlab_helper_functions.html#contour3d>`_
+ arguments.
+
+ :param scalars: The 3D scalar field to visualize
+ :type scalars: numpy.ndarray of float32 with 3 dimensions
+ :param contours:
+ Either the number of isosurfaces to draw (as an int) or
+ the isosurface level (as a float) or a list of isosurface levels
+ (as a list of float)
+ :type contours: Union[int, float, List[float]]
+ :param bool copy:
+ True (default) to make a copy of scalars.
+ False to avoid this copy (do not modify provided data afterwards)
+ :param color:
+ Color.s to use for isosurfaces.
+ Either a single color or a list of colors (one for each isosurface).
+ A color can be defined by its name (as a str) or
+ as RGB(A) as float or uint8.
+ :param str colormap:
+ If color is not provided, this colormap is used
+ for coloring isosurfaces.
+ :param vmin: Minimum value of the colormap
+ :type vmin: Union[float, None]
+ :param vmax: Maximum value of the colormap
+ :type vmax: Union[float, None]
+ :param float opacity:
+ Transparency of the isosurfaces as a float in [0., 1.]
+ :return: The widget used to visualize the data
+ :rtype: ~silx.gui.plot3d.ScalarFieldView.ScalarFieldView
+ """
+ # Prepare isolevel values
+ if isinstance(contours, int):
+ # Compute contours number of isovalues
+ mean = numpy.mean(scalars)
+ std = numpy.std(scalars)
+
+ start = mean - std * ((contours - 1) // 2)
+ contours = [start + std * index for index in range(contours)]
+
+ elif isinstance(contours, float):
+ contours = [contours]
+
+ assert isinstance(contours, Iterable)
+
+ # Prepare colors
+ if color is not None:
+ if isinstance(color, str) or isinstance(color[0], (int, float)):
+ # Single color provided, use it for all isosurfaces
+ colors = [rgba(color)] * len(contours)
+ else:
+ # As many colors as contours
+ colors = [rgba(c) for c in color]
+
+ # convert colors from float to uint8
+ colors = (numpy.array(colors) * 255).astype(numpy.uint8)
+
+ else: # Use colormap
+ colormap = Colormap(name=colormap, vmin=vmin, vmax=vmax)
+ colors = colormap.applyToData(contours)
+
+ assert len(colors) == len(contours)
+
+ # Prepare and apply opacity
+ assert isinstance(opacity, float)
+ opacity = min(max(0., opacity), 1.) # Clip opacity
+ colors[:, -1] = (colors[:, -1] * opacity).astype(numpy.uint8)
+
+ # Prepare widget
+ scalarField = ScalarFieldView()
+
+ scalarField.setBackgroundColor((0.9, 0.9, 0.9))
+ scalarField.setForegroundColor((0.1, 0.1, 0.1))
+ scalarField.setData(scalars, copy=copy)
+
+ # Create a parameter tree for the scalar field view
+ treeView = SFViewParamTree.TreeView(scalarField)
+ treeView.setSfView(scalarField) # Attach the parameter tree to the view
+
+ # Add the parameter tree to the main window in a dock widget
+ dock = qt.QDockWidget()
+ dock.setWindowTitle('Parameters')
+ dock.setWidget(treeView)
+ scalarField.addDockWidget(qt.Qt.RightDockWidgetArea, dock)
+
+ for level, color in zip(contours, colors):
+ scalarField.addIsosurface(level, color)
+
+ scalarField.show()
+
+ return scalarField
+
+
+_POINTS3D_MODE_CONVERSION = {
+ '2dcircle': 'o',
+ '2dcross': 'x',
+ '2ddash': '_',
+ '2ddiamond': 'd',
+ '2dsquare': 's',
+ 'point': ','
+}
+
+
+def points3d(x, y, z=None,
+ values=0.,
+ copy=True,
+ colormap='viridis',
+ vmin=None,
+ vmax=None,
+ mode='o'):
+ """
+ Plot a 3D scatter plot in a :class:`~silx.gui.plot3d.SceneWindow.SceneWindow` widget.
+
+ How to use:
+
+ >>> from silx import sx
+
+ Provided x, y, z, values, 4 numpy array of float32:
+
+ >>> plot3d_window = sx.points3d(x, y, z)
+
+ >>> plot3d_window = sx.points3d(x, y, z, values)
+
+ This function provides a subset of `mayavi.mlab.points3d
+ <http://docs.enthought.com/mayavi/mayavi/auto/mlab_helper_functions.html#points3d>`_
+ arguments.
+
+ :param numpy.ndarray x: X coordinates of the points
+ :param numpy.ndarray y: Y coordinates of the points
+ :param numpy.ndarray z: Z coordinates of the points (optional)
+ :param numpy.ndarray values: Values at each point (optional)
+ :param bool copy:
+ True (default) to make a copy of scalars.
+ False to avoid this copy (do not modify provided data afterwards)
+ :param str colormap:
+ Colormap to use for coding points as colors.
+ :param vmin: Minimum value of the colormap
+ :type vmin: Union[float, None]
+ :param vmax: Maximum value of the colormap
+ :type vmax: Union[float, None]
+ :param str mode: The type of marker to use
+
+ - Circle: 'o', '2dcircle'
+ - Diamond: 'd', '2ddiamond'
+ - Square: 's', '2dsquare'
+ - Plus: '+'
+ - Cross: 'x', '2dcross'
+ - Star: '*'
+ - Vertical line: '|'
+ - Horizontal line: '_', '2ddash'
+ - Point: '.'
+ - Pixel: ','
+ :return: The widget used to visualize the data
+ :rtype: ~silx.gui.plot3d.SceneWindow.SceneWindow
+ """
+ # Prepare widget
+ window = SceneWindow()
+ sceneWidget = window.getSceneWidget()
+ sceneWidget.setBackgroundColor((0.9, 0.9, 0.9))
+ sceneWidget.setForegroundColor((0.5, 0.5, 0.5))
+ sceneWidget.setTextColor((0.1, 0.1, 0.1))
+
+ mode = _POINTS3D_MODE_CONVERSION.get(mode, mode)
+
+ if z is None: # 2D scatter plot
+ scatter = sceneWidget.add2DScatter(x, y, values, copy=copy)
+ else: # 3D scatter plot
+ scatter = sceneWidget.add3DScatter(x, y, z, values, copy=copy)
+
+ colormap = Colormap(name=colormap, vmin=vmin, vmax=vmax)
+ scatter.setColormap(colormap)
+ scatter.setSymbol(mode)
+
+ window.show()
+
+ return window