summaryrefslogtreecommitdiff
path: root/silx/gui/plot/backends/BackendOpenGL.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/backends/BackendOpenGL.py')
-rw-r--r--silx/gui/plot/backends/BackendOpenGL.py144
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