diff options
Diffstat (limited to 'silx/gui/plot/backends/BackendOpenGL.py')
-rw-r--r-- | silx/gui/plot/backends/BackendOpenGL.py | 144 |
1 files changed, 87 insertions, 57 deletions
diff --git a/silx/gui/plot/backends/BackendOpenGL.py b/silx/gui/plot/backends/BackendOpenGL.py index bc10eca..c70b03a 100644 --- a/silx/gui/plot/backends/BackendOpenGL.py +++ b/silx/gui/plot/backends/BackendOpenGL.py @@ -28,7 +28,7 @@ from __future__ import division __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "21/03/2017" +__date__ = "16/08/2017" from collections import OrderedDict, namedtuple from ctypes import c_void_p @@ -39,6 +39,7 @@ import numpy from .._utils import FLOAT32_MINPOS from . import BackendBase from .. import Colors +from ..Colormap import Colormap from ... import qt from ..._glutils import gl @@ -299,6 +300,7 @@ _texFragShd = """ void main(void) { gl_FragColor = texture2D(tex, coords); + gl_FragColor.a = 1.0; } """ @@ -313,7 +315,7 @@ def _getContext(): return _current_context -class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): +class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget): """OpenGL-based Plot backend. WARNINGS: @@ -328,8 +330,13 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): _sigPostRedisplay = qt.Signal() """Signal handling automatic asynchronous replot""" - def __init__(self, plot, parent=None): - qt.QGLWidget.__init__(self, parent) + def __init__(self, plot, parent=None, f=qt.Qt.WindowFlags()): + glu.OpenGLWidget.__init__(self, parent, + alphaBufferSize=8, + depthBufferSize=0, + stencilBufferSize=0, + version=(2, 1), + f=f) BackendBase.BackendBase.__init__(self, plot, parent) self.matScreenProj = mat4Identity() @@ -342,8 +349,6 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): self._keepDataAspectRatio = False - self._devicePixelRatio = 1.0 - self._crosshairCursor = None self._mousePosInPixels = None @@ -361,11 +366,6 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): super(BackendOpenGL, self).postRedisplay, qt.Qt.QueuedConnection) - # TODO is this needed? move it Plot? - self.setGraphXLimits(0., 100.) - self.setGraphYLimits(0., 100., axis='right') - self.setGraphYLimits(0., 100., axis='left') - self.setAutoFillBackground(False) self.setMouseTracking(True) @@ -373,19 +373,24 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): _MOUSE_BTNS = {1: 'left', 2: 'right', 4: 'middle'} + def contextMenuEvent(self, event): + """Override QWidget.contextMenuEvent to implement the context menu""" + # Makes sure it is overridden (issue with PySide) + BackendBase.BackendBase.contextMenuEvent(self, event) + def sizeHint(self): return qt.QSize(8 * 80, 6 * 80) # Mimic MatplotlibBackend 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()] self._plot.onMousePress(xPixel, yPixel, btn) event.accept() def mouseMoveEvent(self, event): - xPixel = event.x() * self._devicePixelRatio - yPixel = event.y() * self._devicePixelRatio + xPixel = event.x() * self.getDevicePixelRatio() + yPixel = event.y() * self.getDevicePixelRatio() # Handle crosshair inXPixel, inYPixel = self._mouseInPlotArea(xPixel, yPixel) @@ -402,16 +407,16 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): event.accept() 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()] self._plot.onMouseRelease(xPixel, yPixel, btn) event.accept() 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, 'angleDelta'): # Qt 5 delta = event.angleDelta().y() @@ -424,11 +429,11 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): def leaveEvent(self, _): self._plot.onMouseLeaveWidget() - # QGLWidget API + # OpenGLWidget API @staticmethod def _setBlendFuncGL(): - # glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + # gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) gl.glBlendFuncSeparate(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA, gl.GL_ONE, @@ -526,13 +531,6 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): glu.setGLContextGetter(_getContext) - if hasattr(self, 'windowHandle'): # Qt 5 - devicePixelRatio = self.windowHandle().devicePixelRatio() - if devicePixelRatio != self._devicePixelRatio: - self._devicePixelRatio = devicePixelRatio - self.resizeGL(int(self.width() * devicePixelRatio), - int(self.height() * devicePixelRatio)) - # Release OpenGL resources for item in self._glGarbageCollector: item.discard() @@ -916,16 +914,32 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): def resizeGL(self, width, height): if width == 0 or height == 0: # Do not resize return - self._plotFrame.size = width, height + + self._plotFrame.size = ( + int(self.getDevicePixelRatio() * width), + int(self.getDevicePixelRatio() * height)) self.matScreenProj = mat4Ortho(0, self._plotFrame.size[0], self._plotFrame.size[1], 0, 1, -1) + # Store current ranges + previousXRange = self.getGraphXLimits() + previousYRange = self.getGraphYLimits(axis='left') + previousYRightRange = self.getGraphYLimits(axis='right') + (xMin, xMax), (yMin, yMax), (y2Min, y2Max) = \ self._plotFrame.dataRanges self.setLimits(xMin, xMax, yMin, yMax, y2Min, y2Max) + # If plot range has changed, then emit signal + if previousXRange != self.getGraphXLimits(): + self._plot.getXAxis()._emitLimitsChanged() + if previousYRange != self.getGraphYLimits(axis='left'): + self._plot.getYAxis(axis='left')._emitLimitsChanged() + if previousYRightRange != self.getGraphYLimits(axis='right'): + self._plot.getYAxis(axis='right')._emitLimitsChanged() + # Add methods def addCurve(self, x, y, legend, @@ -1017,23 +1031,18 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): 'addImage: Convert %s data to float32', str(data.dtype)) data = numpy.array(data, dtype=numpy.float32, order='C') - colormapIsLog = colormap['normalization'].startswith('log') + colormapIsLog = colormap.getNormalization() == 'log' - if colormap['autoscale']: - cmapRange = None - else: - cmapRange = colormap['vmin'], colormap['vmax'] - assert cmapRange[0] <= cmapRange[1] + cmapRange = colormap.getColormapRange(data=data) # Retrieve colormap LUT from name and color array - colormapLut = Colors.applyColormapToData( - numpy.arange(256, dtype=numpy.uint8), - name=colormap['name'], - normalization='linear', - autoscale=False, - vmin=0, - vmax=255, - colors=colormap.get('colors')) + colormapDisp = Colormap(name=colormap.getName(), + normalization=Colormap.LINEAR, + vmin=0, + vmax=255, + colors=colormap.getColormapLUT()) + colormapLut = colormapDisp.applyToData( + numpy.arange(256, dtype=numpy.uint8)) image = GLPlotColormap(data, origin, @@ -1301,8 +1310,7 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): if pickedIndices: picked.append(dict(kind='curve', legend=item.info['legend'], - xdata=item.xData[pickedIndices], - ydata=item.yData[pickedIndices])) + indices=pickedIndices)) return picked @@ -1330,20 +1338,38 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): if fileFormat not in ['png', 'ppm', 'svg', 'tiff']: raise NotImplementedError('Unsupported format: %s' % fileFormat) - self.makeCurrent() - - data = numpy.empty( - (self._plotFrame.size[1], self._plotFrame.size[0], 3), - dtype=numpy.uint8, order='C') + if not self.isValid(): + _logger.error('OpenGL 2.1 not available, cannot save OpenGL image') + width, height = self._plotFrame.size + data = numpy.zeros((height, width, 3), dtype=numpy.uint8) + else: + self.makeCurrent() + + data = numpy.empty( + (self._plotFrame.size[1], self._plotFrame.size[0], 3), + dtype=numpy.uint8, order='C') + + context = self.context() + framebufferTexture = self._plotFBOs.get(context) + if framebufferTexture is None: + # Fallback, supports direct rendering mode: _paintDirectGL + # might have issues as it can read on-screen framebuffer + fboName = self.defaultFramebufferObject() + width, height = self._plotFrame.size + else: + fboName = framebufferTexture.name + height, width = framebufferTexture.shape - gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) - gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1) - gl.glReadPixels(0, 0, self._plotFrame.size[0], self._plotFrame.size[1], - gl.GL_RGB, gl.GL_UNSIGNED_BYTE, data) + previousFramebuffer = gl.glGetInteger(gl.GL_FRAMEBUFFER_BINDING) + gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, fboName) + gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1) + gl.glReadPixels(0, 0, width, height, + gl.GL_RGB, gl.GL_UNSIGNED_BYTE, data) + gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, previousFramebuffer) - # glReadPixels gives bottom to top, - # while images are stored as top to bottom - data = numpy.flipud(data) + # glReadPixels gives bottom to top, + # while images are stored as top to bottom + data = numpy.flipud(data) # fileName is either a file-like object or a str saveImageToFile(data, fileName, fileFormat) @@ -1629,3 +1655,7 @@ class BackendOpenGL(BackendBase.BackendBase, qt.QGLWidget): def getPlotBoundsInPixels(self): return self._plotFrame.plotOrigin + self._plotFrame.plotSize + + def setAxesDisplayed(self, displayed): + BackendBase.BackendBase.setAxesDisplayed(self, displayed) + self._plotFrame.displayed = displayed
\ No newline at end of file |