diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2017-10-07 07:59:01 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2017-10-07 07:59:01 +0200 |
commit | bfa4dba15485b4192f8bbe13345e9658c97ecf76 (patch) | |
tree | fb9c6e5860881fbde902f7cbdbd41dc4a3a9fb5d /silx/gui/plot3d/Plot3DWidget.py | |
parent | f7bdc2acff3c13a6d632c28c4569690ab106eed7 (diff) |
New upstream version 0.6.0+dfsg
Diffstat (limited to 'silx/gui/plot3d/Plot3DWidget.py')
-rw-r--r-- | silx/gui/plot3d/Plot3DWidget.py | 203 |
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) |