Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion datalab/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2797,6 +2797,7 @@ def __edit_settings(self) -> None: # pylint: disable=too-many-branches,too-many
sigima_options.fft_shift_enabled.set(Conf.fft_shift_enabled.get())
sigima_options.auto_normalize_kernel.set(Conf.auto_normalize_kernel.get())
refresh_signal_panel = refresh_image_panel = False
update_signal_items = False

# Handling changes to shape/marker parameters
s_view_result_param = (
Expand Down Expand Up @@ -2858,7 +2859,9 @@ def __edit_settings(self) -> None: # pylint: disable=too-many-branches,too-many
if isinstance(widget, DockablePlotWidget):
widget.update_toolbar_position()
if option.startswith(("sig_autodownsampling", "sig_linewidth")):
refresh_signal_panel = True
# Signal display settings apply to the curves of all signals, not
# only to those of the selected signals
update_signal_items = True
if option == "sig_autoscale_margin_percent":
# Update signal plot widget autoscale margin
sig_margin = Conf.sig_autoscale_margin_percent.get()
Expand Down Expand Up @@ -2898,6 +2901,8 @@ def __edit_settings(self) -> None: # pylint: disable=too-many-branches,too-many
self.imagepanel.update_metadata_view_settings()
if option == "ima_aspect_ratio_1_1":
refresh_image_panel = True
if update_signal_items:
self.signalpanel.plothandler.update_existing_items()
if refresh_signal_panel:
self.signalpanel.manual_refresh()
if refresh_image_panel:
Expand Down
18 changes: 18 additions & 0 deletions datalab/gui/plothandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,24 @@ def update_item_on_plot(self, oid: str, just_show: bool = False) -> None:
item.param.label = self.panel.mainwindow.render_object_title(obj.title)
item.param.update_item(item)

def update_existing_items(self) -> None:
"""Update all existing plot items from their objects and current settings

Unlike :py:meth:`refresh_plot`, the visibility of the plot items and the
shape items are left unchanged. This applies a change of display settings
(e.g. signal auto downsampling or line width) to the plot items of all
objects, and not only to those of the selected objects.
"""
auto_replot = self.plot.autoReplot()
self.plot.setAutoReplot(False)
try:
with block_signals(self.plot):
for oid in self.get_existing_oids():
self.update_item_on_plot(oid)
finally:
self.plot.setAutoReplot(auto_replot)
self.plot.replot()

def set_auto_refresh(self, auto_refresh: bool) -> None:
"""Set auto refresh mode.

Expand Down
7 changes: 5 additions & 2 deletions datalab/gui/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,12 @@ class ViewSettings(gds.DataSet):
).set_prop("display", store=_prop_ads)
sig_autodownsampling_maxpoints = gds.IntItem(
_("Downsampling max points"),
default=10000,
default=100000,
min=1000,
help=_("Maximum number of points for downsampling"),
help=_(
"Maximum number of points displayed for a signal: signals with more "
"points are downsampled for display only (data are not modified)"
),
).set_prop("display", active=_prop_ads)
sig_autoscale_margin_percent = gds.FloatItem(
_("Autoscale margin"),
Expand Down
4 changes: 2 additions & 2 deletions datalab/locale/fr/LC_MESSAGES/datalab.po
Original file line number Diff line number Diff line change
Expand Up @@ -3944,8 +3944,8 @@ msgstr "Utiliser l'auto-sous-échantillonnage pour les signaux volumineux"
msgid "Downsampling max points"
msgstr "Nombre max. de points"

msgid "Maximum number of points for downsampling"
msgstr "Nombre maximum de points pour le sous-échantillonnage"
msgid "Maximum number of points displayed for a signal: signals with more points are downsampled for display only (data are not modified)"
msgstr "Nombre maximal de points affichés pour un signal : les signaux comportant plus de points sont sous-échantillonnés pour l'affichage uniquement (les données ne sont pas modifiées)"

msgid "Autoscale margin"
msgstr "Marge de zoom automatique"
Expand Down
179 changes: 179 additions & 0 deletions datalab/tests/features/signal/autodownsampling_unit_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.

"""
Signal auto downsampling unit tests
-----------------------------------

Signal display settings (auto downsampling, line width) are applied to the curves of
all signals when they are changed in the Settings dialog, and not only to the curves
of the selected signals.
"""

# guitest: show

from __future__ import annotations

import numpy as np
import pytest
from plotpy.items import CurveItem
from sigima.objects import SignalObj, create_signal

from datalab.adapters_plotpy import create_adapter_from_object
from datalab.config import Conf
from datalab.gui import main as main_module
from datalab.gui.main import DLMainWindow
from datalab.gui.panel.signal import SignalPanel
from datalab.gui.settings import ViewSettings
from datalab.objectmodel import get_uuid
from datalab.tests import datalab_test_app_context

#: Number of points of the test signals
SIZES = (1_000, 30_000, 300_000)


def _create_signals(sizes: tuple[int, ...] = SIZES) -> list[SignalObj]:
"""Create test signals with given numbers of points"""
signals = []
for size in sizes:
x = np.linspace(0.0, 1.0, size)
signals.append(create_signal(f"{size} points", x, np.sin(2 * np.pi * 50 * x)))
return signals


def _add_signals(panel: SignalPanel, signals: list[SignalObj]) -> None:
"""Add signals to the panel, create their curves, then select the first one,
so that the curves of the other signals exist but are hidden"""
panel.add_objects(signals, set_current=False)
panel.objview.select_objects(signals)
panel.objview.select_objects(signals[:1])


def _item(panel: SignalPanel, obj: SignalObj) -> CurveItem:
"""Return the curve item of a signal"""
return panel.plothandler[get_uuid(obj)]


def _edit_settings(
win: DLMainWindow, monkeypatch: pytest.MonkeyPatch, **options
) -> None:
"""Change settings as the user does with the Settings dialog

Args:
win: DataLab main window
monkeypatch: pytest monkeypatch fixture
options: option names and their new values
"""

def fake_edit_settings(_parent) -> list[str]:
for name, value in options.items():
getattr(Conf, name).set(value)
return list(options)

monkeypatch.setattr(main_module, "edit_settings", fake_edit_settings)
win.settings_action.trigger()


def _check_downsampling(
panel: SignalPanel, signals: list[SignalObj], maxpoints: int | None
) -> None:
"""Check the number of points drawn for each signal

Args:
panel: signal panel
signals: signals to check
maxpoints: maximum number of points to draw (None if auto downsampling is
disabled)
"""
for obj in signals:
item = _item(panel, obj)
size = obj.x.size
if maxpoints is not None and size > maxpoints:
assert item.param.use_dsamp, obj.title
# At most 2 more points with min/max downsampling (PlotPy >= 2.12)
assert item.dataSize() <= maxpoints + 2, obj.title
else:
assert item.dataSize() == size, obj.title


def _visibility(panel: SignalPanel, signals: list[SignalObj]) -> list[bool]:
"""Return the visibility of the curves of signals"""
return [_item(panel, obj).isVisible() for obj in signals]


def test_settings_default_maxpoints() -> None:
"""The Settings dialog shows the actual default maximum number of points"""
assert ViewSettings().sig_autodownsampling_maxpoints == (
Conf.sig_autodownsampling_maxpoints.get()
)


def test_downsampling_settings_apply_to_all_curves(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Auto downsampling settings are applied to the curves of all signals"""
with datalab_test_app_context(console=False, exec_loop=False) as win:
panel = win.signalpanel
signals = _create_signals()
_add_signals(panel, signals)
maxpoints = Conf.sig_autodownsampling_maxpoints.get()
_check_downsampling(panel, signals, maxpoints)
visibility = _visibility(panel, signals)
assert visibility == [True, False, False]

_edit_settings(win, monkeypatch, sig_autodownsampling_maxpoints=10_000)
_check_downsampling(panel, signals, 10_000)
assert _visibility(panel, signals) == visibility

_edit_settings(win, monkeypatch, sig_autodownsampling=False)
_check_downsampling(panel, signals, None)

_edit_settings(win, monkeypatch, sig_autodownsampling=True)
_check_downsampling(panel, signals, 10_000)
assert _visibility(panel, signals) == visibility


def test_line_width_applies_to_all_curves(monkeypatch: pytest.MonkeyPatch) -> None:
"""Line width settings are applied to the curves of all signals"""
with datalab_test_app_context(console=False, exec_loop=False) as win:
panel = win.signalpanel
signals = _create_signals((100, 200, 300))
_add_signals(panel, signals)
_edit_settings(win, monkeypatch, sig_linewidth=2.0)
for obj in signals:
item = _item(panel, obj)
assert item.param.line.width == 2.0, obj.title
assert item.pen().widthF() == 2.0, obj.title


def test_settings_update_keeps_shapes(monkeypatch: pytest.MonkeyPatch) -> None:
"""Applying signal display settings neither duplicates nor shows shape items"""
with datalab_test_app_context(console=False, exec_loop=False) as win:
panel = win.signalpanel
signals = _create_signals()
_add_signals(panel, signals)
with Conf.show_result_dialog.context(False):
panel.processor.run_feature("stats")
plot = panel.plothandler.plot
nb_items = len(plot.get_items())
visibility = _visibility(panel, signals)
_edit_settings(win, monkeypatch, sig_autodownsampling_maxpoints=10_000)
assert len(plot.get_items()) == nb_items
assert _visibility(panel, signals) == visibility


def test_dialog_items_use_current_settings(monkeypatch: pytest.MonkeyPatch) -> None:
"""Plot items made from existing curves (e.g. for dialogs) use the current
auto downsampling settings"""
with datalab_test_app_context(console=False, exec_loop=False) as win:
panel = win.signalpanel
signals = _create_signals()
_add_signals(panel, signals)
_edit_settings(win, monkeypatch, sig_autodownsampling_maxpoints=10_000)
obj = signals[-1]
item = create_adapter_from_object(obj).make_item(update_from=_item(panel, obj))
assert item.param.use_dsamp
assert item.dataSize() <= 10_000 + 2


if __name__ == "__main__":
pytest.main([__file__, "-v"])
7 changes: 5 additions & 2 deletions doc/features/common/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,13 @@ The Signals sub-tab contains settings specific to signal visualizations:

**Use auto downsampling**
Enable automatic downsampling for large signals to improve performance and
visualization clarity.
visualization clarity. Downsampling only affects the display: signal data and
processing are not modified.

**Downsampling max points**
Maximum number of points to display when downsampling is enabled (default: 10,000).
Maximum number of points displayed for a signal when auto downsampling is enabled
(default: 100,000). Signals with more points are downsampled so that at most this
number of points is displayed.

**Autoscale margin**
Percentage of margin to add around data when auto-scaling signal plots. A value of
Expand Down
8 changes: 4 additions & 4 deletions doc/locale/fr/LC_MESSAGES/features/common/settings.po
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,14 @@ msgstr "Pour les signaux comportant plus de ce nombre de points (par défaut : 1
msgid "**Use auto downsampling**"
msgstr "**Utiliser le sous-échantillonnage automatique**"

msgid "Enable automatic downsampling for large signals to improve performance and visualization clarity."
msgstr "Activer le sous-échantillonnage automatique pour les grands signaux afin d'améliorer les performances et la clarté de la visualisation."
msgid "Enable automatic downsampling for large signals to improve performance and visualization clarity. Downsampling only affects the display: signal data and processing are not modified."
msgstr "Activer le sous-échantillonnage automatique pour les grands signaux afin d'améliorer les performances et la clarté de la visualisation. Le sous-échantillonnage ne concerne que l'affichage : les données des signaux et les traitements ne sont pas modifiés."

msgid "**Downsampling max points**"
msgstr "**Nombre maximum de points de sous-échantillonnage**"

msgid "Maximum number of points to display when downsampling is enabled (default: 10,000)."
msgstr "Nombre maximum de points à afficher lorsque le sous-échantillonnage est activé (par défaut : 10 000)."
msgid "Maximum number of points displayed for a signal when auto downsampling is enabled (default: 100,000). Signals with more points are downsampled so that at most this number of points is displayed."
msgstr "Nombre maximal de points affichés pour un signal lorsque le sous-échantillonnage automatique est activé (par défaut : 100 000). Les signaux comportant plus de points sont sous-échantillonnés de sorte qu'au plus ce nombre de points soit affiché."

msgid "**Autoscale margin**"
msgstr "**Marge d'échelle automatique**"
Expand Down
9 changes: 9 additions & 0 deletions doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ msgstr "**Disposition de la fenêtre :**"
msgid "Fixed the width of the signal/image panels not being restored at startup: after narrowing the panels and restarting DataLab, they came back wider (up to their maximum width) instead of keeping the width chosen by the user"
msgstr "Correction de la largeur des panneaux Signal/Image non restaurée au démarrage : après avoir réduit les panneaux puis redémarré DataLab, ils réapparaissaient plus larges (jusqu'à leur largeur maximale) au lieu de conserver la largeur choisie par l'utilisateur"

msgid "**Signal display:**"
msgstr "**Affichage des signaux :**"

msgid "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))"
msgstr "Correction du sous-échantillonnage automatique des signaux (**Préférences > Visualisation > Signaux**), qui ne commençait qu'au-delà de 5 fois le nombre maximal de points configuré : avec la valeur par défaut de 100 000 points, les signaux de quelques centaines de milliers de points étaient tracés en entier, ce qui ralentissait l'affichage. Les signaux comportant plus de points que ce maximum sont désormais sous-échantillonnés de sorte qu'au plus ce nombre de points soit tracé, et la boîte de dialogue des préférences ainsi que la documentation indiquent désormais la valeur par défaut réelle (100 000 au lieu de 10 000) (corrige [Issue #455](https://github.com/DataLab-Platform/DataLab/issues/455))"

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"
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"

msgid "**HDF5 import:**"
msgstr "**Import HDF5 :**"

Expand Down
5 changes: 5 additions & 0 deletions doc/release_notes/release_1.04.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ See the [HDF5 browser guide](../features/common/h5browser.rst) for scientific ob

* Fixed the width of the signal/image panels not being restored at startup: after narrowing the panels and restarting DataLab, they came back wider (up to their maximum width) instead of keeping the width chosen by the user

**Signal display:**

* 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))
* 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

**HDF5 import:**

* Fixed images with unsigned 32-bit values above 2 147 483 647 being clipped when imported from an HDF5 file ("Clipping uint32 data to int32" warning): such images are now imported as 64-bit floating-point images keeping every value exactly. Other integer images that no supported integer type can hold also use a sufficiently precise floating-point type when possible, instead of rounding to 32-bit floating-point values beyond 16 777 216; a warning is emitted when no supported type guarantees exact conversion (in particular beyond ±2⁵³ for float64)
Expand Down
Loading