summaryrefslogtreecommitdiff
path: root/silx/gui/plot3d/Plot3DWidget.py
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@debian.org>2017-10-07 07:59:01 +0200
committerPicca Frédéric-Emmanuel <picca@debian.org>2017-10-07 07:59:01 +0200
commitbfa4dba15485b4192f8bbe13345e9658c97ecf76 (patch)
treefb9c6e5860881fbde902f7cbdbd41dc4a3a9fb5d /silx/gui/plot3d/Plot3DWidget.py
parentf7bdc2acff3c13a6d632c28c4569690ab106eed7 (diff)
New upstream version 0.6.0+dfsg
Diffstat (limited to 'silx/gui/plot3d/Plot3DWidget.py')
-rw-r--r--silx/gui/plot3d/Plot3DWidget.py203
1 files changed, 116 insertions, 87 deletions
diff --git a/silx/gui/plot3d/Plot3DWidget.py b/silx/gui/plot3d/Plot3DWidget.py
index 9c9da0c..aae3955 100644
--- a/silx/gui/plot3d/Plot3DWidget.py
+++ b/silx/gui/plot3d/Plot3DWidget.py
@@ -35,10 +35,10 @@ import logging
from silx.gui import qt
from silx.gui.plot.Colors import rgba
-from silx.gui.plot3d import Plot3DActions
+from . import actions
from .._utils import convertArrayToQImage
-from .._glutils import gl
+from .. import _glutils as glu
from .scene import interaction, primitives, transform
from . import scene
@@ -79,31 +79,29 @@ class _OverviewViewport(scene.Viewport):
source.extrinsic.direction, source.extrinsic.up)
-class Plot3DWidget(qt.QGLWidget):
- """QGLWidget with a 3D viewport and an overview."""
+class Plot3DWidget(glu.OpenGLWidget):
+ """OpenGL widget with a 3D viewport and an overview."""
- def __init__(self, parent=None):
- if not qt.QGLFormat.hasOpenGL(): # Check if any OpenGL is available
- raise RuntimeError(
- 'OpenGL is not available on this platform: 3D disabled')
+ sigInteractiveModeChanged = qt.Signal()
+ """Signal emitted when the interactive mode has changed
+ """
- self._devicePixelRatio = 1.0 # Store GL canvas/QWidget ratio
- self._isOpenGL21 = False
+ def __init__(self, parent=None, f=qt.Qt.WindowFlags()):
self._firstRender = True
- format_ = qt.QGLFormat()
- format_.setRgba(True)
- format_.setDepth(False)
- format_.setStencil(False)
- format_.setVersion(2, 1)
- format_.setDoubleBuffer(True)
+ super(Plot3DWidget, self).__init__(
+ parent,
+ alphaBufferSize=8,
+ depthBufferSize=0,
+ stencilBufferSize=0,
+ version=(2, 1),
+ f=f)
- super(Plot3DWidget, self).__init__(format_, parent)
self.setAutoFillBackground(False)
self.setMouseTracking(True)
self.setFocusPolicy(qt.Qt.StrongFocus)
- self._copyAction = Plot3DActions.CopyAction(parent=self, plot3d=self)
+ self._copyAction = actions.io.CopyAction(parent=self, plot3d=self)
self.addAction(self._copyAction)
self._updating = False # True if an update is requested
@@ -112,8 +110,8 @@ class Plot3DWidget(qt.QGLWidget):
self.viewport = scene.Viewport()
self.viewport.background = 0.2, 0.2, 0.2, 1.
- sceneScale = transform.Scale(1., 1., 1.)
- self.viewport.scene.transforms = [sceneScale,
+ self._sceneScale = transform.Scale(1., 1., 1.)
+ self.viewport.scene.transforms = [self._sceneScale,
transform.Translate(0., 0., 0.)]
# Overview area
@@ -122,15 +120,56 @@ class Plot3DWidget(qt.QGLWidget):
self.setBackgroundColor((0.2, 0.2, 0.2, 1.))
# Window describing on screen area to render
- self.window = scene.Window(mode='framebuffer')
- self.window.viewports = [self.viewport, self.overview]
+ self._window = scene.Window(mode='framebuffer')
+ self._window.viewports = [self.viewport, self.overview]
+ self._window.addListener(self._redraw)
+
+ self.eventHandler = None
+ self.setInteractiveMode('rotate')
+
+ def setInteractiveMode(self, mode):
+ """Set the interactive mode.
+
+ :param str mode: The interactive mode: 'rotate', 'pan' or None
+ """
+ if mode == self.getInteractiveMode():
+ return
+
+ if mode is None:
+ self.eventHandler = None
+
+ elif mode == 'rotate':
+ self.eventHandler = interaction.RotateCameraControl(
+ self.viewport,
+ orbitAroundCenter=False,
+ mode='position',
+ scaleTransform=self._sceneScale)
+
+ elif mode == 'pan':
+ self.eventHandler = interaction.PanCameraControl(
+ self.viewport,
+ mode='position',
+ scaleTransform=self._sceneScale,
+ selectCB=None)
+
+ else:
+ raise ValueError('Unsupported interactive mode %s', str(mode))
+
+ self.sigInteractiveModeChanged.emit()
- self.eventHandler = interaction.CameraControl(
- self.viewport, orbitAroundCenter=False,
- mode='position', scaleTransform=sceneScale,
- selectCB=None)
+ def getInteractiveMode(self):
+ """Returns the interactive mode in use.
- self.viewport.addListener(self._redraw)
+ :rtype: str
+ """
+ if self.eventHandler is None:
+ return None
+ if isinstance(self.eventHandler, interaction.RotateCameraControl):
+ return 'rotate'
+ elif isinstance(self.eventHandler, interaction.PanCameraControl):
+ return 'pan'
+ else:
+ return None
def setProjection(self, projection):
"""Change the projection in use.
@@ -176,6 +215,25 @@ class Plot3DWidget(qt.QGLWidget):
"""Returns the RGBA background color (QColor)."""
return qt.QColor.fromRgbF(*self.viewport.background)
+ def isOrientationIndicatorVisible(self):
+ """Returns True if the orientation indicator is displayed.
+
+ :rtype: bool
+ """
+ return self.overview in self._window.viewports
+
+ def setOrientationIndicatorVisible(self, visible):
+ """Set the orientation indicator visibility.
+
+ :param bool visible: True to show
+ """
+ visible = bool(visible)
+ if visible != self.isOrientationIndicatorVisible():
+ if visible:
+ self._window.viewports = [self.viewport, self.overview]
+ else:
+ self._window.viewports = [self.viewport]
+
def centerScene(self):
"""Position the center of the scene at the center of rotation."""
self.viewport.resetCamera()
@@ -192,7 +250,7 @@ class Plot3DWidget(qt.QGLWidget):
def _redraw(self, source=None):
"""Viewport listener to require repaint"""
- if not self._updating and self.viewport.dirty:
+ if not self._updating:
self._updating = True # Mark that an update is requested
self.update() # Queued repaint (i.e., asynchronous)
@@ -200,52 +258,18 @@ class Plot3DWidget(qt.QGLWidget):
return qt.QSize(400, 300)
def initializeGL(self):
- # Check if OpenGL2 is available
- versionflags = self.format().openGLVersionFlags()
- self._isOpenGL21 = bool(versionflags & qt.QGLFormat.OpenGL_Version_2_1)
- if not self._isOpenGL21:
- _logger.error(
- '3D rendering is disabled: OpenGL 2.1 not available')
-
- messageBox = qt.QMessageBox(parent=self)
- messageBox.setIcon(qt.QMessageBox.Critical)
- messageBox.setWindowTitle('Error')
- messageBox.setText('3D rendering is disabled.\n\n'
- 'Reason: OpenGL 2.1 is not available.')
- messageBox.addButton(qt.QMessageBox.Ok)
- messageBox.setWindowModality(qt.Qt.WindowModal)
- messageBox.setAttribute(qt.Qt.WA_DeleteOnClose)
- messageBox.show()
+ pass
def paintGL(self):
# In case paintGL is called by the system and not through _redraw,
# Mark as updating.
self._updating = True
- if hasattr(self, 'windowHandle'): # Qt 5
- devicePixelRatio = self.windowHandle().devicePixelRatio()
- if devicePixelRatio != self._devicePixelRatio:
- # Move window from one screen to another one
- self._devicePixelRatio = devicePixelRatio
- # Resize might not be called, so call it explicitly
- self.resizeGL(int(self.width() * devicePixelRatio),
- int(self.height() * devicePixelRatio))
-
- if not self._isOpenGL21:
- # Cannot render scene, just clear the color buffer.
- ox, oy = self.viewport.origin
- w, h = self.viewport.size
- gl.glViewport(ox, oy, w, h)
+ # Update near and far planes only if viewport needs refresh
+ if self.viewport.dirty:
+ self.viewport.adjustCameraDepthExtent()
- gl.glClearColor(*self.viewport.background)
- gl.glClear(gl.GL_COLOR_BUFFER_BIT)
-
- else:
- # Update near and far planes only if viewport needs refresh
- if self.viewport.dirty:
- self.viewport.adjustCameraDepthExtent()
-
- self.window.render(self.context(), self._devicePixelRatio)
+ self._window.render(self.context(), self.getDevicePixelRatio())
if self._firstRender: # TODO remove this ugly hack
self._firstRender = False
@@ -253,8 +277,10 @@ class Plot3DWidget(qt.QGLWidget):
self._updating = False
def resizeGL(self, width, height):
- self.window.size = width, height
- self.viewport.size = width, height
+ width *= self.getDevicePixelRatio()
+ height *= self.getDevicePixelRatio()
+ self._window.size = width, height
+ self.viewport.size = self._window.size
overviewWidth, overviewHeight = self.overview.size
self.overview.origin = width - overviewWidth, height - overviewHeight
@@ -264,27 +290,27 @@ class Plot3DWidget(qt.QGLWidget):
:returns: OpenGL scene RGB rasterization
:rtype: QImage
"""
- if not self._isOpenGL21:
+ if not self.isValid():
_logger.error('OpenGL 2.1 not available, cannot save OpenGL image')
- height, width = self.window.shape
+ height, width = self._window.shape
image = numpy.zeros((height, width, 3), dtype=numpy.uint8)
else:
self.makeCurrent()
- image = self.window.grab(qt.QGLContext.currentContext())
+ image = self._window.grab(self.context())
return convertArrayToQImage(image)
def wheelEvent(self, event):
- xpixel = event.x() * self._devicePixelRatio
- ypixel = event.y() * self._devicePixelRatio
+ xpixel = event.x() * self.getDevicePixelRatio()
+ ypixel = event.y() * self.getDevicePixelRatio()
if hasattr(event, 'delta'): # Qt4
angle = event.delta() / 8.
else: # Qt5
angle = event.angleDelta().y() / 8.
event.accept()
- if angle != 0:
+ if self.eventHandler is not None and angle != 0 and self.isValid():
self.makeCurrent()
self.eventHandler.handleEvent('wheel', xpixel, ypixel, angle)
@@ -315,27 +341,30 @@ class Plot3DWidget(qt.QGLWidget):
_MOUSE_BTNS = {1: 'left', 2: 'right', 4: 'middle'}
def mousePressEvent(self, event):
- xpixel = event.x() * self._devicePixelRatio
- ypixel = event.y() * self._devicePixelRatio
+ xpixel = event.x() * self.getDevicePixelRatio()
+ ypixel = event.y() * self.getDevicePixelRatio()
btn = self._MOUSE_BTNS[event.button()]
event.accept()
- self.makeCurrent()
- self.eventHandler.handleEvent('press', xpixel, ypixel, btn)
+ if self.eventHandler is not None and self.isValid():
+ self.makeCurrent()
+ self.eventHandler.handleEvent('press', xpixel, ypixel, btn)
def mouseMoveEvent(self, event):
- xpixel = event.x() * self._devicePixelRatio
- ypixel = event.y() * self._devicePixelRatio
+ xpixel = event.x() * self.getDevicePixelRatio()
+ ypixel = event.y() * self.getDevicePixelRatio()
event.accept()
- self.makeCurrent()
- self.eventHandler.handleEvent('move', xpixel, ypixel)
+ if self.eventHandler is not None and self.isValid():
+ self.makeCurrent()
+ self.eventHandler.handleEvent('move', xpixel, ypixel)
def mouseReleaseEvent(self, event):
- xpixel = event.x() * self._devicePixelRatio
- ypixel = event.y() * self._devicePixelRatio
+ xpixel = event.x() * self.getDevicePixelRatio()
+ ypixel = event.y() * self.getDevicePixelRatio()
btn = self._MOUSE_BTNS[event.button()]
event.accept()
- self.makeCurrent()
- self.eventHandler.handleEvent('release', xpixel, ypixel, btn)
+ if self.eventHandler is not None and self.isValid():
+ self.makeCurrent()
+ self.eventHandler.handleEvent('release', xpixel, ypixel, btn)