Skip to content

Commit

Permalink
Merge pull request #3956 from t20100/marker-font
Browse files Browse the repository at this point in the history
silx.gui.plot.items: Added Marker item font configuration; silx.config: Added `DEFAULT_PLOT_MARKER_TEXT_FONT_SIZE` config
  • Loading branch information
t20100 authored Oct 27, 2023
2 parents 3666465 + dee470a commit b74b48f
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 49 deletions.
10 changes: 9 additions & 1 deletion src/silx/_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# /*##########################################################################
#
# Copyright (c) 2017-2019 European Synchrotron Radiation Facility
# Copyright (c) 2017-2023 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -157,3 +157,11 @@ class Config(object):
.. versionadded:: 2.0
"""

DEFAULT_PLOT_MARKER_TEXT_FONT_SIZE = None
"""Default font size for marker text.
It will have an influence on PlotWidget marker items
.. versionadded:: 2.0
"""
3 changes: 2 additions & 1 deletion src/silx/gui/plot/backends/BackendBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def addShape(self, x, y, shape, color, fill, overlay,
return object()

def addMarker(self, x, y, text, color,
symbol, linestyle, linewidth, constraint, yaxis):
symbol, linestyle, linewidth, constraint, yaxis, font):
"""Add a point, vertical line or horizontal line marker to the plot.
:param float x: Horizontal position of the marker in graph coordinates.
Expand Down Expand Up @@ -228,6 +228,7 @@ def addMarker(self, x, y, text, color,
the current cursor position in the plot as input
and that returns the filtered coordinates.
:param str yaxis: The Y axis this marker belongs to in: 'left', 'right'
:param font: QFont to use to render text
:return: Handle used by the backend to univocally access the marker
"""
return object()
Expand Down
14 changes: 9 additions & 5 deletions src/silx/gui/plot/backends/BackendMatplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from ... import qt

# First of all init matplotlib and set its backend
from ...utils.matplotlib import FigureCanvasQTAgg
from ...utils.matplotlib import FigureCanvasQTAgg, qFontToFontProperties
import matplotlib
from matplotlib.container import Container
from matplotlib.figure import Figure
Expand Down Expand Up @@ -857,8 +857,9 @@ def addShape(self, x, y, shape, color, fill, overlay,
return item

def addMarker(self, x, y, text, color,
symbol, linestyle, linewidth, constraint, yaxis):
symbol, linestyle, linewidth, constraint, yaxis, font):
textArtist = None
fontProperties = None if font is None else qFontToFontProperties(font)

xmin, xmax = self.getGraphXLimits()
ymin, ymax = self.getGraphYLimits(axis=yaxis)
Expand All @@ -881,7 +882,8 @@ def addMarker(self, x, y, text, color,
if text is not None:
textArtist = _TextWithOffset(x, y, text,
color=color,
horizontalalignment='left')
horizontalalignment='left',
fontproperties=fontProperties)
if symbol is not None:
textArtist.pixel_offset = 10, 3
elif x is not None:
Expand All @@ -894,7 +896,8 @@ def addMarker(self, x, y, text, color,
textArtist = _TextWithOffset(x, 1., text,
color=color,
horizontalalignment='left',
verticalalignment='top')
verticalalignment='top',
fontproperties=fontProperties)
textArtist.pixel_offset = 5, 3
elif y is not None:
line = ax.axhline(y,
Expand All @@ -907,7 +910,8 @@ def addMarker(self, x, y, text, color,
textArtist = _TextWithOffset(1., y, text,
color=color,
horizontalalignment='right',
verticalalignment='top')
verticalalignment='top',
fontproperties=fontProperties)
textArtist.pixel_offset = 5, 3
else:
raise RuntimeError('A marker must at least have one coordinate')
Expand Down
23 changes: 14 additions & 9 deletions src/silx/gui/plot/backends/BackendOpenGL.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __init__(self, x, y, shape, color, fill, overlay,

class _MarkerItem(dict):
def __init__(self, x, y, text, color,
symbol, linestyle, linewidth, constraint, yaxis):
symbol, linestyle, linewidth, constraint, yaxis, font):
super(_MarkerItem, self).__init__()

if symbol is None:
Expand All @@ -109,6 +109,7 @@ def __init__(self, x, y, text, color,
'linestyle': linestyle,
'linewidth': linewidth,
'yaxis': yaxis,
'font': font,
})


Expand Down Expand Up @@ -548,12 +549,13 @@ def _renderItems(self, overlay=False):
self._plotFrame.margins.right - pixelOffset
y = pixelPos[1] - pixelOffset
label = glutils.Text2D(
item['text'], x, y,
item['text'], item['font'], x, y,
color=item['color'],
bgColor=bgColor,
align=glutils.RIGHT,
valign=glutils.BOTTOM,
devicePixelRatio=self.getDevicePixelRatio())
devicePixelRatio=self.getDevicePixelRatio(),
)
labels.append(label)

width = self._plotFrame.size[0]
Expand All @@ -574,12 +576,13 @@ def _renderItems(self, overlay=False):
x = pixelPos[0] + pixelOffset
y = self._plotFrame.margins.top + pixelOffset
label = glutils.Text2D(
item['text'], x, y,
item['text'], item['font'], x, y,
color=item['color'],
bgColor=bgColor,
align=glutils.LEFT,
valign=glutils.TOP,
devicePixelRatio=self.getDevicePixelRatio())
devicePixelRatio=self.getDevicePixelRatio(),
)
labels.append(label)

height = self._plotFrame.size[1]
Expand Down Expand Up @@ -611,12 +614,13 @@ def _renderItems(self, overlay=False):
x = pixelPos[0] + pixelOffset
y = pixelPos[1] + vPixelOffset
label = glutils.Text2D(
item['text'], x, y,
item['text'], item['font'], x, y,
color=item['color'],
bgColor=bgColor,
align=glutils.LEFT,
valign=valign,
devicePixelRatio=self.getDevicePixelRatio())
devicePixelRatio=self.getDevicePixelRatio(),
)
labels.append(label)

# For now simple implementation: using a curve for each marker
Expand Down Expand Up @@ -980,9 +984,10 @@ def addShape(self, x, y, shape, color, fill, overlay,
linestyle, linewidth, linebgcolor)

def addMarker(self, x, y, text, color,
symbol, linestyle, linewidth, constraint, yaxis):
symbol, linestyle, linewidth, constraint, yaxis, font):
font = qt.QApplication.instance().font() if font is None else font
return _MarkerItem(x, y, text, color,
symbol, linestyle, linewidth, constraint, yaxis)
symbol, linestyle, linewidth, constraint, yaxis, font)

# Remove methods

Expand Down
8 changes: 7 additions & 1 deletion src/silx/gui/plot/backends/glutils/GLPlotFrame.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# /*##########################################################################
#
# Copyright (c) 2014-2022 European Synchrotron Radiation Facility
# Copyright (c) 2014-2023 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -44,6 +44,7 @@

import numpy

from .... import qt
from ...._glutils import gl, Program
from ..._utils import checkAxisLimits, FLOAT32_MINPOS
from .GLSupport import mat4Ortho
Expand Down Expand Up @@ -215,6 +216,8 @@ def getVerticesAndLabels(self):
labels = []
tickLabelsSize = [0., 0.]

font = qt.QApplication.instance().font()

xTickLength, yTickLength = self._tickLength
xTickLength *= self.devicePixelRatio
yTickLength *= self.devicePixelRatio
Expand All @@ -225,6 +228,7 @@ def getVerticesAndLabels(self):
tickScale = 1.

label = Text2D(text=text,
font=font,
color=self._foregroundColor,
x=xPixel - xTickLength,
y=yPixel - yTickLength,
Expand Down Expand Up @@ -258,6 +262,7 @@ def getVerticesAndLabels(self):
# yOffset -= 3 * yTickLength

axisTitle = Text2D(text=self.title,
font=font,
color=self._foregroundColor,
x=xAxisCenter + xOffset,
y=yAxisCenter + yOffset,
Expand Down Expand Up @@ -630,6 +635,7 @@ def _buildVerticesAndLabels(self):
self.margins.right) // 2
yTitle = self.margins.top - self._TICK_LENGTH_IN_PIXELS
labels.append(Text2D(text=self.title,
font=qt.QApplication.instance().font(),
color=self._foregroundColor,
x=xTitle,
y=yTitle,
Expand Down
24 changes: 16 additions & 8 deletions src/silx/gui/plot/backends/glutils/GLText.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
It provides Latin-1 (ISO8859-1) characters for one monospace font at one size.
"""

from __future__ import annotations

__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "03/04/2017"
Expand All @@ -36,6 +38,7 @@

import numpy

from .... import qt
from ...._glutils import font, gl, Context, Program, Texture
from .GLSupport import mat4Translate

Expand Down Expand Up @@ -135,13 +138,14 @@ class Text2D(object):
_sizes = _Cache()
"""Cache already computed sizes"""

def __init__(self, text, x=0, y=0,
def __init__(self, text, font, x=0, y=0,
color=(0., 0., 0., 1.),
bgColor=None,
align=LEFT, valign=BASELINE,
rotate=0,
devicePixelRatio= 1.):
self.devicePixelRatio = devicePixelRatio
self.font = font
self._vertices = None
self._text = text
self.x = x
Expand All @@ -161,9 +165,13 @@ def __init__(self, text, x=0, y=0,

self._rotate = numpy.radians(rotate)

def _getTexture(self, text, devicePixelRatio):
def _textureKey(self) -> tuple[str, str, float]:
"""Returns the current texture key"""
return self.text, self.font.key(), self.devicePixelRatio

def _getTexture(self):
# Retrieve/initialize texture cache for current context
textureKey = text, devicePixelRatio
textureKey = self._textureKey()

context = Context.getCurrent()
if context not in self._textures:
Expand All @@ -173,8 +181,8 @@ def _getTexture(self, text, devicePixelRatio):

if textureKey not in textures:
image, offset = font.rasterText(
text,
font.getDefaultFontFamily(),
self.text,
self.font,
devicePixelRatio=self.devicePixelRatio)
if textureKey not in self._sizes:
self._sizes[textureKey] = image.shape[1], image.shape[0]
Expand All @@ -197,11 +205,11 @@ def text(self):

@property
def size(self):
textureKey = self.text, self.devicePixelRatio
textureKey = self._textureKey()
if textureKey not in self._sizes:
image, offset = font.rasterText(
self.text,
font.getDefaultFontFamily(),
self.font,
devicePixelRatio=self.devicePixelRatio)
self._sizes[textureKey] = image.shape[1], image.shape[0]
return self._sizes[textureKey]
Expand Down Expand Up @@ -247,7 +255,7 @@ def render(self, matrix):
prog.use()

texUnit = 0
texture, offset = self._getTexture(self.text, self.devicePixelRatio)
texture, offset = self._getTexture()

gl.glUniform1i(prog.uniforms['texText'], texUnit)

Expand Down
3 changes: 3 additions & 0 deletions src/silx/gui/plot/items/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ class ItemChangedType(enum.Enum):
SELECTABLE = 'selectableChanged'
"""Item's selectable state changed flags."""

FONT = 'fontChanged'
"""Item's text font changed flag."""


class Item(qt.QObject):
"""Description of an item of the plot"""
Expand Down
30 changes: 28 additions & 2 deletions src/silx/gui/plot/items/marker.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# /*##########################################################################
#
# Copyright (c) 2017-2020 European Synchrotron Radiation Facility
# Copyright (c) 2017-2023 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -35,6 +35,7 @@
from ....utils.proxy import docstring
from .core import (Item, DraggableMixIn, ColorMixIn, LineMixIn, SymbolMixIn,
ItemChangedType, YAxisMixIn)
from silx import config
from silx.gui import qt

_logger = logging.getLogger(__name__)
Expand All @@ -58,6 +59,13 @@ def __init__(self):
YAxisMixIn.__init__(self)

self._text = ''
self._font = None
if config.DEFAULT_PLOT_MARKER_TEXT_FONT_SIZE is not None:
self._font = qt.QFont(
qt.QApplication.instance().font().family(),
config.DEFAULT_PLOT_MARKER_TEXT_FONT_SIZE,
)

self._x = None
self._y = None
self._constraint = self._defaultConstraint
Expand All @@ -75,7 +83,9 @@ def _addRendererCall(self, backend,
linestyle=linestyle,
linewidth=linewidth,
constraint=self.getConstraint(),
yaxis=self.getYAxis())
yaxis=self.getYAxis(),
font=self._font, # Do not use getFont to spare creating a new QFont
)

def _addBackendRenderer(self, backend):
"""Update backend renderer"""
Expand Down Expand Up @@ -109,6 +119,22 @@ def setText(self, text):
self._text = text
self._updated(ItemChangedType.TEXT)

def getFont(self) -> Optional[qt.QFont]:
"""Returns a copy of the QFont used to render text.
To modify the text font, use :meth:`setFont`.
"""
return None if self._font is None else qt.QFont(self._font)

def setFont(self, font: Optional[qt.QFont]):
"""Set the QFont used to render text, use None for default.
A copy is stored, so further modification of the provided font are not taken into account.
"""
if font != self._font:
self._font = None if font is None else qt.QFont(font)
self._updated(ItemChangedType.FONT)

def getXPosition(self):
"""Returns the X position of the marker line in data coordinates
Expand Down
Loading

0 comments on commit b74b48f

Please sign in to comment.