summaryrefslogtreecommitdiff
path: root/silx/gui/plot3d/actions
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot3d/actions')
-rw-r--r--silx/gui/plot3d/actions/Plot3DAction.py71
-rw-r--r--silx/gui/plot3d/actions/__init__.py34
-rw-r--r--silx/gui/plot3d/actions/io.py336
-rw-r--r--silx/gui/plot3d/actions/mode.py178
-rw-r--r--silx/gui/plot3d/actions/viewpoint.py231
5 files changed, 0 insertions, 850 deletions
diff --git a/silx/gui/plot3d/actions/Plot3DAction.py b/silx/gui/plot3d/actions/Plot3DAction.py
deleted file mode 100644
index 94b9572..0000000
--- a/silx/gui/plot3d/actions/Plot3DAction.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Base class for QAction attached to a Plot3DWidget."""
-
-from __future__ import absolute_import, division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "06/09/2017"
-
-
-import logging
-import weakref
-
-from silx.gui import qt
-
-
-_logger = logging.getLogger(__name__)
-
-
-class Plot3DAction(qt.QAction):
- """QAction associated to a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, plot3d=None):
- super(Plot3DAction, self).__init__(parent)
- self._plot3d = None
- self.setPlot3DWidget(plot3d)
-
- def setPlot3DWidget(self, widget):
- """Set the Plot3DWidget this action is associated with
-
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget widget:
- The Plot3DWidget to use
- """
- self._plot3d = None if widget is None else weakref.ref(widget)
-
- def getPlot3DWidget(self):
- """Return the Plot3DWidget associated to this action.
-
- If no widget is associated, it returns None.
-
- :rtype: QWidget
- """
- return None if self._plot3d is None else self._plot3d()
diff --git a/silx/gui/plot3d/actions/__init__.py b/silx/gui/plot3d/actions/__init__.py
deleted file mode 100644
index 26243cf..0000000
--- a/silx/gui/plot3d/actions/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 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.
-#
-# ###########################################################################*/
-"""This module provides QAction that can be attached to a plot3DWidget."""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "06/09/2017"
-
-from .Plot3DAction import Plot3DAction # noqa
-from . import viewpoint # noqa
-from . import io # noqa
-from . import mode # noqa
diff --git a/silx/gui/plot3d/actions/io.py b/silx/gui/plot3d/actions/io.py
deleted file mode 100644
index 4020d6f..0000000
--- a/silx/gui/plot3d/actions/io.py
+++ /dev/null
@@ -1,336 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides Plot3DAction related to input/output.
-
-It provides QAction to copy, save (snapshot and video), print a Plot3DWidget.
-"""
-
-from __future__ import absolute_import, division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "06/09/2017"
-
-
-import logging
-import os
-
-import numpy
-
-from silx.gui import qt, printer
-from silx.gui.icons import getQIcon
-from .Plot3DAction import Plot3DAction
-from ..utils import mng
-from ...utils.image import convertQImageToArray
-
-
-_logger = logging.getLogger(__name__)
-
-
-class CopyAction(Plot3DAction):
- """QAction to provide copy of a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, plot3d=None):
- super(CopyAction, self).__init__(parent, plot3d)
-
- self.setIcon(getQIcon('edit-copy'))
- self.setText('Copy')
- self.setToolTip('Copy a snapshot of the 3D scene to the clipboard')
- self.setCheckable(False)
- self.setShortcut(qt.QKeySequence.Copy)
- self.setShortcutContext(qt.Qt.WidgetShortcut)
- self.triggered[bool].connect(self._triggered)
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error('Cannot copy widget, no associated Plot3DWidget')
- else:
- image = plot3d.grabGL()
- qt.QApplication.clipboard().setImage(image)
-
-
-class SaveAction(Plot3DAction):
- """QAction to provide save snapshot of a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, plot3d=None):
- super(SaveAction, self).__init__(parent, plot3d)
-
- self.setIcon(getQIcon('document-save'))
- self.setText('Save...')
- self.setToolTip('Save a snapshot of the 3D scene')
- self.setCheckable(False)
- self.setShortcut(qt.QKeySequence.Save)
- self.setShortcutContext(qt.Qt.WidgetShortcut)
- self.triggered[bool].connect(self._triggered)
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error('Cannot save widget, no associated Plot3DWidget')
- else:
- dialog = qt.QFileDialog(self.parent())
- dialog.setWindowTitle('Save snapshot as')
- dialog.setModal(True)
- dialog.setNameFilters(('Plot3D Snapshot PNG (*.png)',
- 'Plot3D Snapshot JPEG (*.jpg)'))
-
- dialog.setFileMode(qt.QFileDialog.AnyFile)
- dialog.setAcceptMode(qt.QFileDialog.AcceptSave)
-
- if not dialog.exec_():
- return
-
- nameFilter = dialog.selectedNameFilter()
- filename = dialog.selectedFiles()[0]
- dialog.close()
-
- # Forces the filename extension to match the chosen filter
- extension = nameFilter.split()[-1][2:-1]
- if (len(filename) <= len(extension) or
- filename[-len(extension):].lower() != extension.lower()):
- filename += extension
-
- image = plot3d.grabGL()
- if not image.save(filename):
- _logger.error('Failed to save image as %s', filename)
- qt.QMessageBox.critical(
- self.parent(),
- 'Save snapshot as',
- 'Failed to save snapshot')
-
-
-class PrintAction(Plot3DAction):
- """QAction to provide printing of a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, plot3d=None):
- super(PrintAction, self).__init__(parent, plot3d)
-
- self.setIcon(getQIcon('document-print'))
- self.setText('Print...')
- self.setToolTip('Print a snapshot of the 3D scene')
- self.setCheckable(False)
- self.setShortcut(qt.QKeySequence.Print)
- self.setShortcutContext(qt.Qt.WidgetShortcut)
- self.triggered[bool].connect(self._triggered)
-
- def getPrinter(self):
- """Return the QPrinter instance used for printing.
-
- :rtype: QPrinter
- """
- return printer.getDefaultPrinter()
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error('Cannot print widget, no associated Plot3DWidget')
- else:
- printer = self.getPrinter()
- dialog = qt.QPrintDialog(printer, plot3d)
- dialog.setWindowTitle('Print Plot3D snapshot')
- if not dialog.exec_():
- return
-
- image = plot3d.grabGL()
-
- # Draw pixmap with painter
- painter = qt.QPainter()
- if not painter.begin(printer):
- return
-
- if (printer.pageRect().width() < image.width() or
- printer.pageRect().height() < image.height()):
- # Downscale to page
- xScale = printer.pageRect().width() / image.width()
- yScale = printer.pageRect().height() / image.height()
- scale = min(xScale, yScale)
- else:
- scale = 1.
-
- rect = qt.QRectF(0,
- 0,
- scale * image.width(),
- scale * image.height())
- painter.drawImage(rect, image)
- painter.end()
-
-
-class VideoAction(Plot3DAction):
- """This action triggers the recording of a video of the scene.
-
- The scene is rotated 360 degrees around a vertical axis.
-
- :param parent: Action parent see :class:`QAction`.
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- PNG_SERIE_FILTER = 'Serie of PNG files (*.png)'
- MNG_FILTER = 'Multiple-image Network Graphics file (*.mng)'
-
- def __init__(self, parent, plot3d=None):
- super(VideoAction, self).__init__(parent, plot3d)
- self.setText('Record video..')
- self.setIcon(getQIcon('camera'))
- self.setToolTip(
- 'Record a video of a 360 degrees rotation of the 3D scene.')
- self.setCheckable(False)
- self.triggered[bool].connect(self._triggered)
-
- def _triggered(self, checked=False):
- """Action triggered callback"""
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.warning(
- 'Ignoring action triggered without Plot3DWidget set')
- return
-
- dialog = qt.QFileDialog(parent=plot3d)
- dialog.setWindowTitle('Save video as...')
- dialog.setModal(True)
- dialog.setNameFilters([self.PNG_SERIE_FILTER,
- self.MNG_FILTER])
- dialog.setFileMode(dialog.AnyFile)
- dialog.setAcceptMode(dialog.AcceptSave)
-
- if not dialog.exec_():
- return
-
- nameFilter = dialog.selectedNameFilter()
- filename = dialog.selectedFiles()[0]
-
- # Forces the filename extension to match the chosen filter
- extension = nameFilter.split()[-1][2:-1]
- if (len(filename) <= len(extension) or
- filename[-len(extension):].lower() != extension.lower()):
- filename += extension
-
- nbFrames = int(4. * 25) # 4 seconds, 25 fps
-
- if nameFilter == self.PNG_SERIE_FILTER:
- self._saveAsPNGSerie(filename, nbFrames)
- elif nameFilter == self.MNG_FILTER:
- self._saveAsMNG(filename, nbFrames)
- else:
- _logger.error('Unsupported file filter: %s', nameFilter)
-
- def _saveAsPNGSerie(self, filename, nbFrames):
- """Save video as serie of PNG files.
-
- It adds a counter to the provided filename before the extension.
-
- :param str filename: filename to use as template
- :param int nbFrames: Number of frames to generate
- """
- plot3d = self.getPlot3DWidget()
- assert plot3d is not None
-
- # Define filename template
- nbDigits = int(numpy.log10(nbFrames)) + 1
- indexFormat = '%%0%dd' % nbDigits
- extensionIndex = filename.rfind('.')
- filenameFormat = \
- filename[:extensionIndex] + indexFormat + filename[extensionIndex:]
-
- try:
- for index, image in enumerate(self._video360(nbFrames)):
- image.save(filenameFormat % index)
- except GeneratorExit:
- pass
-
- def _saveAsMNG(self, filename, nbFrames):
- """Save video as MNG file.
-
- :param str filename: filename to use
- :param int nbFrames: Number of frames to generate
- """
- plot3d = self.getPlot3DWidget()
- assert plot3d is not None
-
- frames = (convertQImageToArray(im) for im in self._video360(nbFrames))
- try:
- with open(filename, 'wb') as file_:
- for chunk in mng.convert(frames, nb_images=nbFrames):
- file_.write(chunk)
- except GeneratorExit:
- os.remove(filename) # Saving aborted, delete file
-
- def _video360(self, nbFrames):
- """Run the video and provides the images
-
- :param int nbFrames: The number of frames to generate for
- :return: Iterator of QImage of the video sequence
- """
- plot3d = self.getPlot3DWidget()
- assert plot3d is not None
-
- angleStep = 360. / nbFrames
-
- # Create progress bar dialog
- dialog = qt.QDialog(plot3d)
- dialog.setWindowTitle('Record Video')
- layout = qt.QVBoxLayout(dialog)
- progress = qt.QProgressBar()
- progress.setRange(0, nbFrames)
- layout.addWidget(progress)
-
- btnBox = qt.QDialogButtonBox(qt.QDialogButtonBox.Abort)
- btnBox.rejected.connect(dialog.reject)
- layout.addWidget(btnBox)
-
- dialog.setModal(True)
- dialog.show()
-
- qapp = qt.QApplication.instance()
-
- for frame in range(nbFrames):
- progress.setValue(frame)
- image = plot3d.grabGL()
- yield image
- plot3d.viewport.orbitCamera('left', angleStep)
- qapp.processEvents()
- if not dialog.isVisible():
- break # It as been rejected by the abort button
- else:
- dialog.accept()
-
- if dialog.result() == qt.QDialog.Rejected:
- raise GeneratorExit('Aborted')
diff --git a/silx/gui/plot3d/actions/mode.py b/silx/gui/plot3d/actions/mode.py
deleted file mode 100644
index ce09b4c..0000000
--- a/silx/gui/plot3d/actions/mode.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# 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
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides Plot3DAction related to interaction modes.
-
-It provides QAction to rotate or pan a Plot3DWidget
-as well as toggle a picking mode.
-"""
-
-from __future__ import absolute_import, division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "06/09/2017"
-
-
-import logging
-
-from ....utils.proxy import docstring
-from ... import qt
-from ...icons import getQIcon
-from .Plot3DAction import Plot3DAction
-
-
-_logger = logging.getLogger(__name__)
-
-
-class InteractiveModeAction(Plot3DAction):
- """Base class for QAction changing interactive mode of a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param str interaction: The interactive mode this action controls
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, interaction, plot3d=None):
- self._interaction = interaction
-
- super(InteractiveModeAction, self).__init__(parent, plot3d)
- self.setCheckable(True)
- self.triggered[bool].connect(self._triggered)
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error(
- 'Cannot set %s interaction, no associated Plot3DWidget' %
- self._interaction)
- else:
- plot3d.setInteractiveMode(self._interaction)
- self.setChecked(True)
-
- @docstring(Plot3DAction)
- def setPlot3DWidget(self, widget):
- # Disconnect from previous Plot3DWidget
- plot3d = self.getPlot3DWidget()
- if plot3d is not None:
- plot3d.sigInteractiveModeChanged.disconnect(
- self._interactiveModeChanged)
-
- super(InteractiveModeAction, self).setPlot3DWidget(widget)
-
- # Connect to new Plot3DWidget
- if widget is None:
- self.setChecked(False)
- else:
- self.setChecked(widget.getInteractiveMode() == self._interaction)
- widget.sigInteractiveModeChanged.connect(
- self._interactiveModeChanged)
-
- def _interactiveModeChanged(self):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error('Received a signal while there is no widget')
- else:
- self.setChecked(plot3d.getInteractiveMode() == self._interaction)
-
-
-class RotateArcballAction(InteractiveModeAction):
- """QAction to set arcball rotation interaction on a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, plot3d=None):
- super(RotateArcballAction, self).__init__(parent, 'rotate', plot3d)
-
- self.setIcon(getQIcon('rotate-3d'))
- self.setText('Rotate')
- self.setToolTip('Rotate the view. Press <b>Ctrl</b> to pan.')
-
-
-class PanAction(InteractiveModeAction):
- """QAction to set pan interaction on a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- def __init__(self, parent, plot3d=None):
- super(PanAction, self).__init__(parent, 'pan', plot3d)
-
- self.setIcon(getQIcon('pan'))
- self.setText('Pan')
- self.setToolTip('Pan the view. Press <b>Ctrl</b> to rotate.')
-
-
-class PickingModeAction(Plot3DAction):
- """QAction to toggle picking moe on a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- sigSceneClicked = qt.Signal(float, float)
- """Signal emitted when the scene is clicked with the left mouse button.
-
- This signal is only emitted when the action is checked.
-
- It provides the (x, y) clicked mouse position
- """
-
- def __init__(self, parent, plot3d=None):
- super(PickingModeAction, self).__init__(parent, plot3d)
- self.setIcon(getQIcon('pointing-hand'))
- self.setText('Picking')
- self.setToolTip('Toggle picking with left button click')
- self.setCheckable(True)
- self.triggered[bool].connect(self._triggered)
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is not None:
- if checked:
- plot3d.sigSceneClicked.connect(self.sigSceneClicked)
- else:
- plot3d.sigSceneClicked.disconnect(self.sigSceneClicked)
-
- @docstring(Plot3DAction)
- def setPlot3DWidget(self, widget):
- # Disconnect from previous Plot3DWidget
- plot3d = self.getPlot3DWidget()
- if plot3d is not None and self.isChecked():
- plot3d.sigSceneClicked.disconnect(self.sigSceneClicked)
-
- super(PickingModeAction, self).setPlot3DWidget(widget)
-
- # Connect to new Plot3DWidget
- if widget is None:
- self.setChecked(False)
- elif self.isChecked():
- widget.sigSceneClicked.connect(self.sigSceneClicked)
diff --git a/silx/gui/plot3d/actions/viewpoint.py b/silx/gui/plot3d/actions/viewpoint.py
deleted file mode 100644
index d764c40..0000000
--- a/silx/gui/plot3d/actions/viewpoint.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# 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 module provides Plot3DAction controlling the viewpoint.
-
-It provides QAction to rotate or pan a Plot3DWidget.
-"""
-
-from __future__ import absolute_import, division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "03/10/2017"
-
-
-import time
-import logging
-
-from silx.gui import qt
-from silx.gui.icons import getQIcon
-from .Plot3DAction import Plot3DAction
-
-
-_logger = logging.getLogger(__name__)
-
-
-class _SetViewpointAction(Plot3DAction):
- """Base class for actions setting a Plot3DWidget viewpoint
-
- :param parent: See :class:`QAction`
- :param str face: The name of the predefined viewpoint
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, face, plot3d=None):
- super(_SetViewpointAction, self).__init__(parent, plot3d)
- assert face in ('side', 'front', 'back', 'left', 'right', 'top', 'bottom')
- self._face = face
-
- self.setIconVisibleInMenu(True)
- self.setCheckable(False)
- self.triggered[bool].connect(self._triggered)
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error(
- 'Cannot start/stop rotation, no associated Plot3DWidget')
- else:
- plot3d.viewport.camera.extrinsic.reset(face=self._face)
- plot3d.centerScene()
-
-
-class FrontViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the front
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(FrontViewpointAction, self).__init__(parent, 'front', plot3d)
-
- self.setIcon(getQIcon('cube-front'))
- self.setText('Front')
- self.setToolTip('View along the -Z axis')
-
-
-class BackViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the back
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(BackViewpointAction, self).__init__(parent, 'back', plot3d)
-
- self.setIcon(getQIcon('cube-back'))
- self.setText('Back')
- self.setToolTip('View along the +Z axis')
-
-
-class LeftViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the left
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(LeftViewpointAction, self).__init__(parent, 'left', plot3d)
-
- self.setIcon(getQIcon('cube-left'))
- self.setText('Left')
- self.setToolTip('View along the +X axis')
-
-
-class RightViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the right
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(RightViewpointAction, self).__init__(parent, 'right', plot3d)
-
- self.setIcon(getQIcon('cube-right'))
- self.setText('Right')
- self.setToolTip('View along the -X axis')
-
-
-class TopViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the top
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(TopViewpointAction, self).__init__(parent, 'top', plot3d)
-
- self.setIcon(getQIcon('cube-top'))
- self.setText('Top')
- self.setToolTip('View along the -Y axis')
-
-
-class BottomViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the bottom
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(BottomViewpointAction, self).__init__(parent, 'bottom', plot3d)
-
- self.setIcon(getQIcon('cube-bottom'))
- self.setText('Bottom')
- self.setToolTip('View along the +Y axis')
-
-
-class SideViewpointAction(_SetViewpointAction):
- """QAction to set Plot3DWidget viewpoint to look from the side
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
- def __init__(self, parent, plot3d=None):
- super(SideViewpointAction, self).__init__(parent, 'side', plot3d)
-
- self.setIcon(getQIcon('cube'))
- self.setText('Side')
- self.setToolTip('Side view')
-
-
-class RotateViewpoint(Plot3DAction):
- """QAction to rotate the scene of a Plot3DWidget
-
- :param parent: See :class:`QAction`
- :param ~silx.gui.plot3d.Plot3DWidget.Plot3DWidget plot3d:
- Plot3DWidget the action is associated with
- """
-
- _TIMEOUT_MS = 50
- """Time interval between to frames (in milliseconds)"""
-
- _DEGREE_PER_SECONDS = 360. / 5.
- """Rotation speed of the animation"""
-
- def __init__(self, parent, plot3d=None):
- super(RotateViewpoint, self).__init__(parent, plot3d)
-
- self._previousTime = None
-
- self._timer = qt.QTimer(self)
- self._timer.setInterval(self._TIMEOUT_MS) # 20fps
- self._timer.timeout.connect(self._rotate)
-
- self.setIcon(getQIcon('cube-rotate'))
- self.setText('Rotate scene')
- self.setToolTip('Rotate the 3D scene around the vertical axis')
- self.setCheckable(True)
- self.triggered[bool].connect(self._triggered)
-
-
- def _triggered(self, checked=False):
- plot3d = self.getPlot3DWidget()
- if plot3d is None:
- _logger.error(
- 'Cannot start/stop rotation, no associated Plot3DWidget')
- elif checked:
- self._previousTime = time.time()
- self._timer.start()
- else:
- self._timer.stop()
- self._previousTime = None
-
- def _rotate(self):
- """Perform a step of the rotation"""
- if self._previousTime is None:
- _logger.error('Previous time not set!')
- angleStep = 0.
- else:
- angleStep = self._DEGREE_PER_SECONDS * (time.time() - self._previousTime)
-
- self.getPlot3DWidget().viewport.orbitCamera('left', angleStep)
- self._previousTime = time.time()