Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

silx.gui.plot.PlotWidget: Added support of line style defined as (offset, (dash pattern)) #4020

Merged
merged 11 commits into from
Dec 20, 2023
4 changes: 2 additions & 2 deletions src/silx/gui/plot/LegendSelector.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def setData(self, modelIndex, value, role):
elif role == self.iconLineWidthRole:
item[1]["linewidth"] = int(value)
elif role == self.iconLineStyleRole:
item[1]["linestyle"] = str(value)
item[1]["linestyle"] = value
elif role == self.iconSymbolRole:
item[1]["symbol"] = str(value)
elif role == qt.Qt.CheckStateRole:
Expand Down Expand Up @@ -674,7 +674,7 @@ def _handleMouseClick(self, modelIndex):
"legend": str(modelIndex.data(qt.Qt.DisplayRole)),
"icon": {
"linewidth": str(modelIndex.data(LegendModel.iconLineWidthRole)),
"linestyle": str(modelIndex.data(LegendModel.iconLineStyleRole)),
"linestyle": modelIndex.data(LegendModel.iconLineStyleRole),
"symbol": str(modelIndex.data(LegendModel.iconSymbolRole)),
},
"selected": modelIndex.data(qt.Qt.CheckStateRole),
Expand Down
12 changes: 8 additions & 4 deletions src/silx/gui/plot/backends/BackendBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,14 @@ def addCurve(
- 's' square

:param float linewidth: The width of the curve in pixels
:param str linestyle: Type of line::
:param linestyle: Type of line::

- ' ' or '' no line
- '-' solid line
- '--' dashed line
- '-.' dash-dot line
- ':' dotted line
- (offset, (dash pattern))

:param str yaxis: The Y axis this curve belongs to in: 'left', 'right'
:param xerror: Values with the uncertainties on the x values
Expand Down Expand Up @@ -190,7 +191,7 @@ def addShape(
:param str color: Color of the item
:param bool fill: True to fill the shape
:param bool overlay: True if item is an overlay, False otherwise
:param str linestyle: Style of the line.
:param linestyle: Style of the line.
Only relevant for line markers where X or Y is None.
Value in:

Expand All @@ -199,6 +200,7 @@ def addShape(
- '--' dashed line
- '-.' dash-dot line
- ':' dotted line
- (offset, (dash pattern))
:param float linewidth: Width of the line.
Only relevant for line markers where X or Y is None.
:param str gapcolor: Background color of the line, e.g., 'blue', 'b',
Expand All @@ -214,7 +216,7 @@ def addMarker(
text: str | None,
color: str,
symbol: str | None,
linestyle: str,
linestyle: str | tuple[float, tuple[float, ...] | None],
linewidth: float,
constraint: Callable[[float, float], tuple[float, float]] | None,
yaxis: str,
Expand Down Expand Up @@ -250,6 +252,7 @@ def addMarker(
- '--' dashed line
- '-.' dash-dot line
- ':' dotted line
- (offset, (dash pattern))
:param linewidth: Width of the line.
Only relevant for line markers where X or Y is None.
:param constraint: A function filtering marker displacement by
Expand Down Expand Up @@ -300,8 +303,9 @@ def setGraphCursor(self, flag, color, linewidth, linestyle):
- '--' dashed line
- '-.' dash-dot line
- ':' dotted line
- (offset, (dash pattern))

:type linestyle: None or one of the predefined styles.
:type linestyle: None, one of the predefined styles or (offset, (dash pattern)).
"""
pass

Expand Down
4 changes: 4 additions & 0 deletions src/silx/gui/plot/backends/BackendMatplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,10 @@ def draw(self):

self.updateZOrder()

if not qt_inspect.isValid(self):
_logger.info("draw requested but widget no longer exists")
return

# Starting with mpl 2.1.0, toggling autoscale raises a ValueError
# in some situations. See #1081, #1136, #1163,
if self._matplotlibVersion >= Version("2.0.0"):
Expand Down
80 changes: 62 additions & 18 deletions src/silx/gui/plot/backends/BackendOpenGL.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,17 @@

class _ShapeItem(dict):
def __init__(
self, x, y, shape, color, fill, overlay, linewidth, dashpattern, gapcolor
self,
x,
y,
shape,
color,
fill,
overlay,
linewidth,
dashoffset,
dashpattern,
gapcolor,
):
super(_ShapeItem, self).__init__()

Expand All @@ -85,6 +95,7 @@ def __init__(
"x": x,
"y": y,
"linewidth": linewidth,
"dashoffset": dashoffset,
"dashpattern": dashpattern,
"gapcolor": gapcolor,
}
Expand All @@ -100,6 +111,7 @@ def __init__(
color,
symbol,
linewidth,
dashoffset,
dashpattern,
constraint,
yaxis,
Expand All @@ -125,6 +137,7 @@ def __init__(
"constraint": constraint if isConstraint else None,
"symbol": symbol,
"linewidth": linewidth,
"dashoffset": dashoffset,
"dashpattern": dashpattern,
"yaxis": yaxis,
"font": font,
Expand Down Expand Up @@ -588,6 +601,7 @@ def _renderItems(self, overlay=False):
color=item["color"],
gapColor=item["gapcolor"],
width=item["linewidth"],
dashOffset=item["dashoffset"],
dashPattern=item["dashpattern"],
)
context.matrix = self.matScreenProj
Expand Down Expand Up @@ -638,6 +652,7 @@ def _renderItems(self, overlay=False):
(pixelPos[1], pixelPos[1]),
color=color,
width=item["linewidth"],
dashOffset=item["dashoffset"],
dashPattern=item["dashpattern"],
)
context.matrix = self.matScreenProj
Expand Down Expand Up @@ -671,6 +686,7 @@ def _renderItems(self, overlay=False):
(0, height),
color=color,
width=item["linewidth"],
dashOffset=item["dashoffset"],
dashPattern=item["dashpattern"],
)
context.matrix = self.matScreenProj
Expand Down Expand Up @@ -859,21 +875,37 @@ def _castArrayTo(v):
else:
raise ValueError("Unsupported data type")

_DASH_PATTERNS = { # Convert from linestyle to dash pattern
"": None,
" ": None,
"-": (),
"--": (3.7, 1.6, 3.7, 1.6),
"-.": (6.4, 1.6, 1, 1.6),
":": (1, 1.65, 1, 1.65),
None: None,
_DASH_PATTERNS = {
"": (0.0, None),
" ": (0.0, None),
"-": (0.0, ()),
"--": (0.0, (3.7, 1.6, 3.7, 1.6)),
"-.": (0.0, (6.4, 1.6, 1, 1.6)),
":": (0.0, (1, 1.65, 1, 1.65)),
None: (0.0, None),
}
"""Convert from linestyle to (offset, (dash pattern))

def _lineStyleToDashPattern(
self, style: str | None
) -> tuple[float, float, float, float] | tuple[()] | None:
"""Convert a linestyle to its corresponding dash pattern"""
return self._DASH_PATTERNS[style]
Note: dash pattern internal convention differs from matplotlib:
- None: no line at all
- (): "solid" line
"""

def _lineStyleToDashOffsetPattern(
self, style
) -> tuple[float, tuple[float, float, float, float] | tuple[()] | None]:
"""Convert a linestyle to its corresponding offset and dash pattern"""
if style is None or isinstance(style, str):
return self._DASH_PATTERNS[style]

# (offset, (dash pattern)) case
offset, pattern = style
if pattern is None:
# Convert from matplotlib to internal representation of solid
pattern = ()
if len(pattern) == 2:
pattern = pattern * 2
return offset, pattern

def addCurve(
self,
Expand Down Expand Up @@ -994,6 +1026,7 @@ def addCurve(
if fill is True:
fillColor = color

dashoffset, dashpattern = self._lineStyleToDashOffsetPattern(linestyle)
curve = glutils.GLPlotCurve2D(
x,
y,
Expand All @@ -1003,7 +1036,8 @@ def addCurve(
lineColor=color,
lineGapColor=gapcolor,
lineWidth=linewidth,
lineDashPattern=self._lineStyleToDashPattern(linestyle),
lineDashOffset=dashoffset,
lineDashPattern=dashpattern,
marker=symbol,
markerColor=color,
markerSize=symbolsize,
Expand Down Expand Up @@ -1108,9 +1142,18 @@ def addShape(
if self._plotFrame.yAxis.isLog and y.min() <= 0.0:
raise RuntimeError("Cannot add item with Y <= 0 with Y axis log scale")

dashpattern = self._lineStyleToDashPattern(linestyle)
dashoffset, dashpattern = self._lineStyleToDashOffsetPattern(linestyle)
return _ShapeItem(
x, y, shape, color, fill, overlay, linewidth, dashpattern, gapcolor
x,
y,
shape,
color,
fill,
overlay,
linewidth,
dashoffset,
dashpattern,
gapcolor,
)

def addMarker(
Expand All @@ -1128,14 +1171,15 @@ def addMarker(
bgcolor: RGBAColorType | None,
):
font = qt.QApplication.instance().font() if font is None else font
dashpattern = self._lineStyleToDashPattern(linestyle)
dashoffset, dashpattern = self._lineStyleToDashOffsetPattern(linestyle)
return _MarkerItem(
x,
y,
text,
color,
symbol,
linewidth,
dashoffset,
dashpattern,
constraint,
yaxis,
Expand Down
12 changes: 11 additions & 1 deletion src/silx/gui/plot/backends/glutils/GLPlotCurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ class GLLines2D(object):
"unscaled" dash pattern as 4 lengths in points (dash1, gap1, dash2, gap2).
This pattern is scaled with the line width.
Set to () to draw solid lines (default), and to None to disable rendering.
:param float dashOffset: The offset in points the patterns starts at.
The offset is scaled with the line width.
:param drawMode: OpenGL drawing mode
:param List[float] offset: Translation of coordinates (ox, oy)
"""
Expand Down Expand Up @@ -353,13 +355,14 @@ class GLLines2D(object):
/* Dashes: [0, x], [y, z]
Dash period: w */
uniform vec4 dash;
uniform float dashOffset;
uniform vec4 gapColor;

varying float vDist;
varying vec4 vColor;

void main(void) {
float dist = mod(vDist, dash.w);
float dist = mod(vDist + dashOffset, dash.w);
if ((dist > dash.x && dist < dash.y) || dist > dash.z) {
if (gapColor.a == 0.) {
discard; // Discard full transparent bg color
Expand All @@ -383,6 +386,7 @@ def __init__(
color=(0.0, 0.0, 0.0, 1.0),
gapColor=None,
width=1,
dashOffset=0.0,
dashPattern=(),
drawMode=None,
offset=(0.0, 0.0),
Expand Down Expand Up @@ -416,6 +420,7 @@ def __init__(
self.gapColor = gapColor
self.width = width
self.dashPattern = dashPattern
self.dashOffset = dashOffset
self.offset = offset

self._drawMode = drawMode if drawMode is not None else gl.GL_LINE_STRIP
Expand Down Expand Up @@ -447,6 +452,7 @@ def render(self, context):
offset * scale for offset in numpy.cumsum(self.dashPattern)
)
gl.glUniform4f(program.uniforms["dash"], *dashOffsets)
gl.glUniform1f(program.uniforms["dashOffset"], self.dashOffset * scale)

if self.gapColor is None:
# Use fully transparent color which gets discarded in shader
Expand Down Expand Up @@ -1188,6 +1194,7 @@ def __init__(
lineColor=(0.0, 0.0, 0.0, 1.0),
lineGapColor=None,
lineWidth=1,
lineDashOffset=0.0,
lineDashPattern=(),
marker=SQUARE,
markerColor=(0.0, 0.0, 0.0, 1.0),
Expand Down Expand Up @@ -1278,6 +1285,7 @@ def deduce_baseline(baseline):
self.lines.color = lineColor
self.lines.gapColor = lineGapColor
self.lines.width = lineWidth
self.lines.dashOffset = lineDashOffset
self.lines.dashPattern = lineDashPattern
self.lines.offset = self.offset

Expand Down Expand Up @@ -1305,6 +1313,8 @@ def deduce_baseline(baseline):

lineWidth = _proxyProperty(("lines", "width"))

lineDashOffset = _proxyProperty(("lines", "dashOffset"))

lineDashPattern = _proxyProperty(("lines", "dashPattern"))

marker = _proxyProperty(("points", "marker"))
Expand Down
Loading
Loading