diff options
Diffstat (limited to 'silx/gui/plot3d/actions')
-rw-r--r-- | silx/gui/plot3d/actions/Plot3DAction.py | 71 | ||||
-rw-r--r-- | silx/gui/plot3d/actions/__init__.py | 34 | ||||
-rw-r--r-- | silx/gui/plot3d/actions/io.py | 336 | ||||
-rw-r--r-- | silx/gui/plot3d/actions/mode.py | 178 | ||||
-rw-r--r-- | silx/gui/plot3d/actions/viewpoint.py | 231 |
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() |