Source code for fsleyes.gl.text

#
# text.py - The Text class
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`Text` class, which uses the
:mod:`fsleyes_widgets.utils.textbitmap` module, and a :class:`.Texture2D`
object to render text to a GL canvas.
"""


import OpenGL.GL as gl
import numpy     as np

import fsleyes.gl.textures              as textures
import fsleyes_widgets.utils.textbitmap as textbmp


[docs]class Text: """A ``Text`` object manages a RGBA :class:`.Texture2D` object, allowing text to be drawn to a GL canvas. The :mod:`fsleyes_widgets.utils.textbitmap` module is used to render the text, using ``matplotlib``. Usage:: # create and maintain a reference to a Text object import fsleyes.gl.text as gltext text = gltext.Text('hello') # modify various properties by direct attribute # assignment - see __init__ for definitions text.pos = (0.5, 0.5) text.colour = '#FFFFFF' # activate your GL context, and call draw() text.draw() # call destroy() when you're finished text.destroy() """
[docs] def __init__(self, text=None, pos=None, off=None, coordinates='proportions', fontSize=10, halign=None, valign=None, colour=None, bgColour=None, alpha=None, scale=None, angle=None): """Create a ``Text`` object. :arg text: The text to draw. :arg pos: (x, y) position along the horizontal/vertical axes as either a proportion between 0 (left/bottom) and 1 (right/top), or as absolute pixels. :arg off: Fixed (x, y) horizontal/vertical offsets in pixels :arg coordinates: Whether to interpret ``pos`` as ``'proportions'`` (the default), or as absolute ``'pixels'``. :arg fontSize: Font size in points. :arg halign: Horizontal alignemnt - ``'left'``, ``'centre'``, or ``right``. :arg valign: Vertical alignemnt - ``'bottom'``, ``'centre'``, or ``top``. :arg colour: Colour to draw the text in (any ``matplotlib``-compatible colour specification) :arg bgColour: Background colour (default: transparent). :arg alpha: Opacity between 0 and 1. :arg scale: Scale the text by this factor. :arg angle: Angle, in degrees, by which to rotate the text. NOT IMPLEMENTED YET AND PROBABLY NEVER WILL BE """ # Every time any display properties change, # the text bitmap is re-generated and cached. # (see __refreshBitmap). Re-generating the # bitmap on every call to draw would be # unnecessarily expensive. self.__bitmap = None # Access to these attributes is protected, # as they induce a bitmap refresh self.__text = text self.__fontSize = fontSize self.__colour = colour self.__bgColour = bgColour self.__alpha = alpha # All othjer attributes can be assigned directly self.pos = pos self.off = off self.coordinates = coordinates self.halign = halign self.valign = valign self.scale = scale self.angle = angle self.__texture = textures.Texture2D( '{}_{}'.format(type(self).__name__, id(self)), interp=gl.GL_LINEAR)
[docs] def destroy(self): """Must be called when this ``Text`` is no longer needed. Frees texture resources. """ self.__texture.destroy() self.__texture = None
def __clearBitmap(self, old, new): """Used by property setters to clear cached bitmap, if a value which requires the bitmap to be re-generated is changed. """ if old != new: self.__bitmap = None @property def colour(self): """Return the current foreground colour. """ return self.__colour @colour.setter def colour(self, value): """Set the foreground colour. """ self.__clearBitmap(self.__colour, value) self.__colour = value @property def bgColour(self): """Return the current background colour. """ return self.__bgColour @bgColour.setter def bgColour(self, value): """Set the background colour. """ self.__clearBitmap(self.__bgColour, value) self.__bgColour = value @property def alpha(self): """Return the current opacity. """ return self.__alpha @alpha.setter def alpha(self, value): """Set the opacity. """ self.__clearBitmap(self.__alpha, value) self.__alpha = value @property def text(self): """Returns the current text value.""" return self.__text @text.setter def text(self, value): """Update the text.""" self.__clearBitmap(self.__text, value) self.__text = value @property def fontSize(self): """Returns the current font size.""" return self.__fontSize @fontSize.setter def fontSize(self, value): """Update the font size.""" self.__clearBitmap(self.__fontSize, value) self.__fontSize = value @property def size(self): """Return the size of the text texture in pixels, scaled by the ``scale`` factor if it is set. Returns ``None`` if the text has not yet been drawn and the bitmap not created. """ if self.__bitmap is None: return None size = self.__bitmap.shape[1:] if self.scale is not None: size = (size[0] * self.scale, size[1] * self.scale) return size def __refreshBitmap(self): """Called when the text bitmap and texture data needs a refresh. """ bmp = textbmp.textBitmap(self.text, fontSize=self.fontSize, fgColour=self.colour, bgColour=self.bgColour, alpha=self.alpha) bmp = np.flipud(bmp).transpose([2, 1, 0]) self.__bitmap = bmp self.__texture.set(data=bmp)
[docs] def draw(self, width, height): """Draws the texture onto the current GL canvas. :arg width: Width of canvas in pixels :arg height: Height of canvas in pixels """ if self.text is None or self.text == '': return if self.pos is None: return if (width == 0) or (height == 0): return if self.__bitmap is None: self.__refreshBitmap() if self.off is not None: off = list(self.off) else: off = [0, 0] pos = list(self.pos) if self.coordinates == 'proportions': pos[0] = pos[0] * width pos[1] = pos[1] * height size = self.size if self.halign == 'centre': pos[0] -= size[0] / 2.0 elif self.halign == 'right': pos[0] -= size[0] if self.valign == 'centre': pos[1] -= size[1] / 2.0 elif self.valign == 'top': pos[1] -= size[1] xlo = pos[0] + off[0] ylo = pos[1] + off[1] xhi = xlo + size[0] yhi = ylo + size[1] # Set up an ortho view where the # display coordinates correspond # to the canvas pixel coordinates. mm = gl.glGetInteger(gl.GL_MATRIX_MODE) gl.glMatrixMode(gl.GL_PROJECTION) gl.glPushMatrix() gl.glLoadIdentity() gl.glOrtho(0, width, 0, height, -1, 1) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPushMatrix() gl.glLoadIdentity() gl.glEnable(gl.GL_BLEND) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) self.__texture.drawOnBounds(0, xlo, xhi, ylo, yhi, 0, 1) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPopMatrix() gl.glMatrixMode(gl.GL_PROJECTION) gl.glPopMatrix() gl.glMatrixMode(mm)