summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorAlexandre Marie <alexandre.marie@synchrotron-soleil.fr>2019-07-09 10:20:20 +0200
committerAlexandre Marie <alexandre.marie@synchrotron-soleil.fr>2019-07-09 10:20:20 +0200
commit654a6ac93513c3cc1ef97cacd782ff674c6f4559 (patch)
tree3b986e4972de7c57fa465820367602fc34bcb0d3 /examples
parenta763e5d1b3921b3194f3d4e94ab9de3fbe08bbdd (diff)
New upstream version 0.11.0+dfsg
Diffstat (limited to 'examples')
-rw-r--r--examples/plot3dSceneWindow.py12
-rw-r--r--examples/plot3dUpdateScatterFromThread.py176
-rw-r--r--examples/plotLimits.py6
-rw-r--r--examples/plotStats.py63
-rw-r--r--examples/plotWidget.py72
5 files changed, 292 insertions, 37 deletions
diff --git a/examples/plot3dSceneWindow.py b/examples/plot3dSceneWindow.py
index 5e78aa6..1b2f808 100644
--- a/examples/plot3dSceneWindow.py
+++ b/examples/plot3dSceneWindow.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -51,8 +51,6 @@ import numpy
from silx.gui import qt
from silx.gui.plot3d.SceneWindow import SceneWindow, items
-from silx.gui.plot3d.tools.PositionInfoWidget import PositionInfoWidget
-from silx.gui.widgets.BoxLayoutDockWidget import BoxLayoutDockWidget
SIZE = 1024
@@ -69,14 +67,6 @@ sceneWidget.setForegroundColor((1., 1., 1., 1.))
sceneWidget.setTextColor((0.1, 0.1, 0.1, 1.))
-# Add PositionInfoWidget to display picking info
-positionInfo = PositionInfoWidget()
-positionInfo.setSceneWidget(sceneWidget)
-dock = BoxLayoutDockWidget()
-dock.setWindowTitle("Selection Info")
-dock.setWidget(positionInfo)
-window.addDockWidget(qt.Qt.BottomDockWidgetArea, dock)
-
# 2D Image ###
# Add a dummy RGBA image
diff --git a/examples/plot3dUpdateScatterFromThread.py b/examples/plot3dUpdateScatterFromThread.py
new file mode 100644
index 0000000..9c2213f
--- /dev/null
+++ b/examples/plot3dUpdateScatterFromThread.py
@@ -0,0 +1,176 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2019 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""This script illustrates the update of a
+:class:`~silx.gui.plot3d.SceneWindow.SceneWindow` widget from a thread.
+
+The problem is that GUI methods should be called from the main thread.
+To safely update the scene from another thread, one need to execute the update
+asynchronously in the main thread.
+In this example, this is achieved with
+:func:`~silx.gui.utils.concurrent.submitToQtMainThread`.
+
+In this example a thread calls submitToQtMainThread to append data to a 3D scatter.
+"""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "08/03/2019"
+
+
+import threading
+import time
+
+import numpy
+
+from silx.gui import qt
+from silx.gui.utils import concurrent
+from silx.gui.plot3d.SceneWindow import SceneWindow
+from silx.gui.plot3d import items
+
+
+MAX_NUMBER_OF_POINTS = 10**6
+
+
+class UpdateScatterThread(threading.Thread):
+ """Thread updating the scatter 3D item data
+
+ :param ~silx.gui.plot3d.items.Scatter3D scatter3d: 3D scatter to update.
+ """
+
+ def __init__(self, scatter3d):
+ self.scatter3d = scatter3d
+ self.running = False
+ self.future_result = None
+ super(UpdateScatterThread, self).__init__()
+
+ def start(self):
+ """Start the update thread"""
+ self.running = True
+ super(UpdateScatterThread, self).start()
+
+ def _appendScatterData(self, x, y, z, value):
+ """Add some data points to the Scatter3D item.
+
+ This method MUST be called in the Qt main thread.
+
+ :param numpy.ndarray x:
+ :param numpy.ndarray y:
+ :param numpy.ndarray z:
+ :param numpy.ndarray value:
+ """
+ # use copy=False to avoid useless copy of numpy arrays
+ curX, curY, curZ, curValue = self.scatter3d.getData(copy=False)
+
+ x = numpy.append(curX, x)
+ y = numpy.append(curY, y)
+ z = numpy.append(curZ, z)
+ value = numpy.append(curValue, value)
+
+ # Update data
+ self.scatter3d.setData(x, y, z, value, copy=False)
+
+ def run(self):
+ """Method implementing thread loop that updates the scatter data
+
+ It produces adds scatter points every 10 ms or so, up to 1 million.
+ """
+ count = 0 # Number of data points currently rendered
+
+ # Init arrays that accumulate scatter points
+ x = numpy.array((), dtype=numpy.float32)
+ y = numpy.array((), dtype=numpy.float32)
+ z = numpy.array((), dtype=numpy.float32)
+ value = numpy.array((), dtype=numpy.float32)
+
+ while self.running:
+ time.sleep(0.01)
+
+ # Generate new data points
+ inclination = numpy.random.random(1000).astype(numpy.float32) * numpy.pi
+ azimuth = numpy.random.random(1000).astype(numpy.float32) * 2. * numpy.pi
+ radius = numpy.random.normal(loc=10., scale=.5, size=1000)
+ newX = radius * numpy.sin(inclination) * numpy.cos(azimuth)
+ newY = radius * numpy.sin(inclination) * numpy.sin(azimuth)
+ newZ = radius * numpy.cos(inclination)
+ newValue = numpy.random.random(1000).astype(numpy.float32)
+
+ # Accumulate data points
+ x = numpy.append(x, newX)
+ y = numpy.append(y, newY)
+ z = numpy.append(z, newZ)
+ value = numpy.append(value, newValue)
+
+ # Only append data if the previous one has been added
+ if self.future_result is None or self.future_result.done():
+ if count > MAX_NUMBER_OF_POINTS:
+ # Restart a new scatter plot asyn
+ self.future_result = concurrent.submitToQtMainThread(
+ self.scatter3d.setData, x, y, z, value)
+
+ count = len(x)
+ else:
+ # Append data asynchronously
+ self.future_result = concurrent.submitToQtMainThread(
+ self._appendScatterData, x, y, z, value)
+
+ count += len(x)
+
+ # Reset accumulators
+ x = numpy.array((), dtype=numpy.float32)
+ y = numpy.array((), dtype=numpy.float32)
+ z = numpy.array((), dtype=numpy.float32)
+ value = numpy.array((), dtype=numpy.float32)
+
+ def stop(self):
+ """Stop the update thread"""
+ self.running = False
+ self.join(2)
+
+
+def main():
+ global app
+ app = qt.QApplication([])
+
+ # Create a SceneWindow
+ window = SceneWindow()
+ window.show()
+
+ sceneWidget = window.getSceneWidget()
+ scatter = items.Scatter3D()
+ scatter.setSymbol(',')
+ scatter.getColormap().setName('magma')
+ sceneWidget.addItem(scatter)
+
+ # Create the thread that calls submitToQtMainThread
+ updateThread = UpdateScatterThread(scatter)
+ updateThread.start() # Start updating the plot
+
+ app.exec_()
+
+ updateThread.stop() # Stop updating the plot
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/plotLimits.py b/examples/plotLimits.py
index 0a39bc6..c7cc7f5 100644
--- a/examples/plotLimits.py
+++ b/examples/plotLimits.py
@@ -23,8 +23,8 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-"""This script is an example to illustrate how to use axis synchronization
-tool.
+"""This script is an example to illustrate how to set range constraints on
+plot axes.
"""
from silx.gui import qt
@@ -37,7 +37,7 @@ class ConstrainedViewPlot(qt.QMainWindow):
def __init__(self):
qt.QMainWindow.__init__(self)
- self.setWindowTitle("Plot with synchronized axes")
+ self.setWindowTitle("Plot with constrained axes")
widget = qt.QWidget(self)
self.setCentralWidget(widget)
diff --git a/examples/plotStats.py b/examples/plotStats.py
index f7474ec..3929697 100644
--- a/examples/plotStats.py
+++ b/examples/plotStats.py
@@ -46,7 +46,46 @@ from silx.gui import qt
from silx.gui.colors import Colormap
from silx.gui.plot import Plot1D
from silx.gui.plot.stats.stats import StatBase
+from silx.gui.utils import concurrent
+import random
+import threading
+import argparse
import numpy
+import time
+
+
+class UpdateThread(threading.Thread):
+ """Thread updating the curve of a :class:`~silx.gui.plot.Plot1D`
+
+ :param plot1d: The Plot1D to update."""
+
+ def __init__(self, plot1d):
+ self.plot1d = plot1d
+ self.running = False
+ super(UpdateThread, self).__init__()
+
+ def start(self):
+ """Start the update thread"""
+ self.running = True
+ super(UpdateThread, self).start()
+
+ def run(self):
+ """Method implementing thread loop that updates the plot"""
+ while self.running:
+ time.sleep(1)
+ # Run plot update asynchronously
+ concurrent.submitToQtMainThread(
+ self.plot1d.addCurve,
+ numpy.arange(1000),
+ numpy.random.random(1000),
+ resetzoom=False,
+ legend=random.choice(('mycurve0', 'mycurve1'))
+ )
+
+ def stop(self):
+ """Stop the update thread"""
+ self.running = False
+ self.join(2)
class Integral(StatBase):
@@ -88,17 +127,22 @@ class COM(StatBase):
return comX, comY
-def main():
+def main(argv):
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ '--update-mode',
+ default='manual',
+ help='update mode to display (manual or auto)')
+
+ options = parser.parse_args(argv[1:])
+
app = qt.QApplication([])
plot = Plot1D()
- x = numpy.arange(21)
- y = numpy.arange(21)
- plot.addCurve(x=x, y=y, legend='myCurve')
- plot.addCurve(x=x, y=(y + 5), legend='myCurve2')
-
- plot.setActiveCurve('myCurve')
+ # Create the thread that calls submitToQtMainThread
+ updateThread = UpdateThread(plot)
+ updateThread.start() # Start updating the plot
plot.addScatter(x=[0, 2, 5, 5, 12, 20],
y=[2, 3, 4, 20, 15, 6],
@@ -113,11 +157,14 @@ def main():
]
plot.getStatsWidget().setStats(stats)
+ plot.getStatsWidget().setUpdateMode(options.update_mode)
+ plot.getStatsWidget().setDisplayOnlyActiveItem(False)
plot.getStatsWidget().parent().setVisible(True)
plot.show()
app.exec_()
+ updateThread.stop() # Stop updating the plot
if __name__ == '__main__':
- main()
+ main(sys.argv)
diff --git a/examples/plotWidget.py b/examples/plotWidget.py
index c8a90a5..af64afb 100644
--- a/examples/plotWidget.py
+++ b/examples/plotWidget.py
@@ -36,7 +36,7 @@ as its central widget and adds toolbars and a colorbar by using pluggable widget
__authors__ = ["T. Vincent"]
__license__ = "MIT"
-__date__ = "05/09/2017"
+__date__ = "06/05/2019"
import numpy
@@ -68,15 +68,29 @@ class MyPlotWindow(qt.QMainWindow):
palette.setColor(qt.QPalette.Window, qt.Qt.white)
colorBar.setPalette(palette)
+ options = qt.QWidget(self)
+ layout = qt.QVBoxLayout(options)
+ button = qt.QPushButton("Show image", self)
+ button.clicked.connect(self.showImage)
+ layout.addWidget(button)
+ button = qt.QPushButton("Show scatter", self)
+ button.clicked.connect(self.showScatter)
+ layout.addWidget(button)
+ button = qt.QPushButton("Show delaunay", self)
+ button.clicked.connect(self.showDelaunay)
+ layout.addWidget(button)
+ layout.addStretch()
+
# Combine the ColorBarWidget and the PlotWidget as
# this QMainWindow's central widget
gridLayout = qt.QGridLayout()
gridLayout.setSpacing(0)
gridLayout.setContentsMargins(0, 0, 0, 0)
- gridLayout.addWidget(self._plot, 0, 0)
- gridLayout.addWidget(colorBar, 0, 1)
+ gridLayout.addWidget(options, 0, 0)
+ gridLayout.addWidget(self._plot, 0, 1)
+ gridLayout.addWidget(colorBar, 0, 2)
gridLayout.setRowStretch(0, 1)
- gridLayout.setColumnStretch(0, 1)
+ gridLayout.setColumnStretch(1, 1)
centralWidget = qt.QWidget(self)
centralWidget.setLayout(gridLayout)
self.setCentralWidget(centralWidget)
@@ -116,6 +130,44 @@ class MyPlotWindow(qt.QMainWindow):
"""Returns the PlotWidget contains in this window"""
return self._plot
+ def showImage(self):
+ plot = self.getPlotWidget()
+ plot.clear()
+ plot.getDefaultColormap().setName('viridis')
+
+ # Add an image to the plot
+ x = numpy.outer(
+ numpy.linspace(-10, 10, 200), numpy.linspace(-10, 5, 150))
+ image = numpy.sin(x) / x
+ plot.addImage(image)
+ plot.resetZoom()
+
+ def showScatter(self):
+ plot = self.getPlotWidget()
+ plot.clear()
+ plot.getDefaultColormap().setName('viridis')
+
+ nbPoints = 50
+ x = numpy.random.rand(nbPoints)
+ y = numpy.random.rand(nbPoints)
+ value = numpy.random.rand(nbPoints)
+ plot.addScatter(x=x, y=y, value=value)
+ plot.resetZoom()
+
+ def showDelaunay(self):
+ plot = self.getPlotWidget()
+ plot.clear()
+ plot.getDefaultColormap().setName('viridis')
+
+ nbPoints = 50
+ x = numpy.random.rand(nbPoints)
+ y = numpy.random.rand(nbPoints)
+ value = numpy.random.rand(nbPoints)
+ legend = plot.addScatter(x=x, y=y, value=value)
+ scatter = plot.getScatter(legend)
+ scatter.setVisualization("solid")
+ plot.resetZoom()
+
def main():
global app
@@ -125,17 +177,7 @@ def main():
window = MyPlotWindow()
window.setAttribute(qt.Qt.WA_DeleteOnClose)
window.show()
-
- # Change the default colormap
- plot = window.getPlotWidget()
- plot.getDefaultColormap().setName('viridis')
-
- # Add an image to the plot
- x = numpy.outer(
- numpy.linspace(-10, 10, 200), numpy.linspace(-10, 5, 150))
- image = numpy.sin(x) / x
- plot.addImage(image)
-
+ window.showImage()
app.exec_()