Skip to content

Commit ca8fdea

Browse files
Merge pull request #458 from DataLab-Platform/fix/457-xaxis-grid-alignment
fix(signal): keep the X axis aligned with the grid after a refresh
2 parents 96a1a73 + 3e1cbf6 commit ca8fdea

4 files changed

Lines changed: 113 additions & 3 deletions

File tree

‎datalab/gui/plothandler.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from plotpy.items import CurveItem, GridItem, LegendBoxItem, MaskedXYImageItem
3838
from plotpy.plot import PlotOptions
3939
from qtpy import QtWidgets as QW
40-
from qwt import QwtScaleDraw
40+
from qwt import QwtDateTimeScaleDraw, QwtScaleDraw
4141
from sigima.objects import ImageObj, SignalObj, TypeObj
4242

4343
from datalab.adapters_metadata import GeometryAdapter, TableAdapter
@@ -725,8 +725,12 @@ def refresh_plot(
725725
if has_datetime and datetime_format is not None:
726726
self.plot.set_axis_datetime("bottom", format=datetime_format)
727727
else:
728-
# Restore default scale draw (remove datetime formatting)
729-
self.plot.setAxisScaleDraw(self.plot.get_axis_id("bottom"), QwtScaleDraw())
728+
# Restore default scale draw only when leaving the datetime mode:
729+
# replacing it on every refresh could misalign the axis ticks with
730+
# the grid and the curves (see Issue #457)
731+
axis_id = self.plot.get_axis_id("bottom")
732+
if isinstance(self.plot.axisScaleDraw(axis_id), QwtDateTimeScaleDraw):
733+
self.plot.setAxisScaleDraw(axis_id, QwtScaleDraw())
730734

731735

732736
class ImagePlotHandler(BasePlotHandler[ImageObj, MaskedXYImageItem]):
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2+
3+
"""
4+
X axis scale draw unit tests
5+
----------------------------
6+
7+
The X axis of the signal view keeps its scale draw when the view is refreshed, so
8+
that its ticks stay aligned with the grid and the curves (the scale draw is only
9+
replaced when leaving the date/time mode).
10+
"""
11+
12+
# guitest: show
13+
14+
from __future__ import annotations
15+
16+
from datetime import datetime, timedelta
17+
18+
import numpy as np
19+
import pytest
20+
from plotpy.plot import BasePlot
21+
from qwt import QwtDateTimeScaleDraw
22+
from sigima.objects import SignalObj, create_signal
23+
24+
from datalab.tests import datalab_test_app_context
25+
26+
# X bounds which are not graduations of the axis once autoscaled (as for the
27+
# 'step.npy' test file): end labels do not overflow the axis ends
28+
X_MIN, X_MAX = 3.1e-7, 4.08e-7
29+
30+
31+
def _create_step_signal() -> SignalObj:
32+
"""Create a signal whose X axis bounds are not graduations"""
33+
x = np.linspace(X_MIN, X_MAX, 500)
34+
return create_signal("Step", x, np.tanh((x - 3.15e-7) * 1e8))
35+
36+
37+
def _create_datetime_signal() -> SignalObj:
38+
"""Create a signal with a date/time X axis"""
39+
start = datetime(2025, 1, 1)
40+
timestamps = [start + timedelta(seconds=i) for i in range(100)]
41+
obj = create_signal("Date/time", np.arange(100.0), np.sin(np.arange(100.0) / 10))
42+
obj.set_x_from_datetime(timestamps, unit="s")
43+
return obj
44+
45+
46+
def _x_axis_ends(plot: BasePlot) -> tuple[list[float], list[float]]:
47+
"""Return the ends of the X axis scale and of the X canvas map, in canvas
48+
coordinates"""
49+
widget = plot.axisWidget(plot.xBottom)
50+
offset = widget.x() - plot.canvas().x()
51+
smap = widget.scaleDraw().scaleMap()
52+
cmap = plot.canvasMap(plot.xBottom)
53+
return (
54+
sorted((smap.p1() + offset, smap.p2() + offset)),
55+
sorted((cmap.p1(), cmap.p2())),
56+
)
57+
58+
59+
def test_refresh_keeps_x_scale_draw() -> None:
60+
"""Refreshing the signal view keeps the scale draw of the X axis"""
61+
with datalab_test_app_context(console=False, exec_loop=False) as win:
62+
panel = win.signalpanel
63+
plot = panel.plothandler.plot
64+
panel.add_object(_create_step_signal())
65+
scale_draw = plot.axisScaleDraw(plot.xBottom)
66+
panel.manual_refresh()
67+
assert plot.axisScaleDraw(plot.xBottom) is scale_draw
68+
panel.add_object(_create_step_signal())
69+
assert plot.axisScaleDraw(plot.xBottom) is scale_draw
70+
71+
72+
def test_x_axis_aligned_with_canvas() -> None:
73+
"""The X axis ticks are aligned with the grid and the curves after loading"""
74+
with datalab_test_app_context(console=False, exec_loop=False) as win:
75+
panel = win.signalpanel
76+
plot = panel.plothandler.plot
77+
panel.add_object(_create_step_signal())
78+
scale_ends, canvas_ends = _x_axis_ends(plot)
79+
assert scale_ends == pytest.approx(canvas_ends, abs=0.5)
80+
panel.manual_refresh()
81+
scale_ends, canvas_ends = _x_axis_ends(plot)
82+
assert scale_ends == pytest.approx(canvas_ends, abs=0.5)
83+
84+
85+
def test_datetime_x_axis_restored() -> None:
86+
"""The date/time X axis formatting is removed when showing regular signals"""
87+
with datalab_test_app_context(console=False, exec_loop=False) as win:
88+
panel = win.signalpanel
89+
plot = panel.plothandler.plot
90+
panel.add_object(_create_datetime_signal())
91+
assert isinstance(plot.axisScaleDraw(plot.xBottom), QwtDateTimeScaleDraw)
92+
panel.add_object(_create_step_signal())
93+
scale_draw = plot.axisScaleDraw(plot.xBottom)
94+
assert not isinstance(scale_draw, QwtDateTimeScaleDraw)
95+
panel.manual_refresh()
96+
assert plot.axisScaleDraw(plot.xBottom) is scale_draw
97+
98+
99+
if __name__ == "__main__":
100+
test_refresh_keeps_x_scale_draw()
101+
test_x_axis_aligned_with_canvas()
102+
test_datetime_x_axis_restored()

‎doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,9 @@ msgstr "Correction du sous-échantillonnage automatique des signaux (**Préfére
495495
msgid "Fixed changes of the auto downsampling and line width settings only applying to the curves of the selected signals: the other curves kept the former settings, even once selected again. These settings now apply to all curves as soon as the Settings dialog is closed"
496496
msgstr "Correction des modifications des préférences de sous-échantillonnage automatique et d'épaisseur de trait qui ne s'appliquaient qu'aux courbes des signaux sélectionnés : les autres courbes conservaient les anciennes préférences, même une fois sélectionnées à nouveau. Ces préférences s'appliquent désormais à toutes les courbes dès la fermeture de la boîte de dialogue des préférences"
497497

498+
msgid "Fixed the X axis ticks being shifted relative to the vertical grid lines and the curves after opening a signal or changing the selection, for signals whose X axis does not start and end on a graduation (e.g., `step.npy` from the Sigima test data): the shift remained until the view was panned or zoomed. The X axis now stays aligned with the grid and the curves (fixes [Issue #457](https://github.com/DataLab-Platform/DataLab/issues/457))"
499+
msgstr "Correction des graduations de l'axe X qui étaient décalées par rapport aux lignes verticales de la grille et aux courbes après l'ouverture d'un signal ou un changement de sélection, pour les signaux dont l'axe X ne commence et ne se termine pas sur une graduation (par exemple, `step.npy` des données de test de Sigima) : le décalage persistait jusqu'à un déplacement ou un zoom de la vue. L'axe X reste désormais aligné avec la grille et les courbes (corrige [Issue #457](https://github.com/DataLab-Platform/DataLab/issues/457))"
500+
498501
msgid "**HDF5 import:**"
499502
msgstr "**Import HDF5 :**"
500503

‎doc/release_notes/release_1.04.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ See the [HDF5 browser guide](../features/common/h5browser.rst) for scientific ob
216216

217217
* Fixed auto downsampling of signals (**Settings > Visualization > Signals**) only starting above 5 times the configured maximum number of points: with the default value of 100,000 points, signals of a few hundred thousand points were drawn in full, which slowed down the display. Signals with more points than the maximum are now downsampled so that at most this number of points is drawn, and the Settings dialog and the documentation now give the actual default value (100,000 instead of 10,000) (fixes [Issue #455](https://github.com/DataLab-Platform/DataLab/issues/455))
218218
* Fixed changes of the auto downsampling and line width settings only applying to the curves of the selected signals: the other curves kept the former settings, even once selected again. These settings now apply to all curves as soon as the Settings dialog is closed
219+
* Fixed the X axis ticks being shifted relative to the vertical grid lines and the curves after opening a signal or changing the selection, for signals whose X axis does not start and end on a graduation (e.g., `step.npy` from the Sigima test data): the shift remained until the view was panned or zoomed. The X axis now stays aligned with the grid and the curves (fixes [Issue #457](https://github.com/DataLab-Platform/DataLab/issues/457))
219220

220221
**HDF5 import:**
221222

0 commit comments

Comments
 (0)