Skip to content

Commit

Permalink
improve slider step precision based on data range (napari#884)
Browse files Browse the repository at this point in the history
* improve slider step precision based on data range
* docs and tests
  • Loading branch information
tlambert03 authored Jan 17, 2020
1 parent 8554776 commit a10a88b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 4 deletions.
14 changes: 12 additions & 2 deletions napari/_qt/layers/qt_image_base_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,21 @@ def create_range_popup(layer, attr, parent=None):
)
is_integer_type = np.issubdtype(layer.dtype, np.integer)

d_range = getattr(layer, range_attr)
popup = QRangeSliderPopup(
initial_values=getattr(layer, attr),
data_range=getattr(layer, range_attr),
data_range=d_range,
collapsible=False,
precision=(0 if is_integer_type else 2),
precision=(
0
if is_integer_type
# scale precision with the log of the data range order of magnitude
# eg. 0 - 1 (0 order of mag) -> 3 decimal places
# 0 - 10 (1 order of mag) -> 2 decimals
# 0 - 100 (2 orders of mag) -> 1 decimal
# ≥ 3 orders of mag -> no decimals
else int(max(3 - np.log10(max(d_range[1] - d_range[0], 0.01)), 0))
),
parent=parent,
)

Expand Down
24 changes: 23 additions & 1 deletion napari/_qt/layers/tests/test_qt_image_base_layer_.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QPushButton

from napari._qt.layers.qt_image_base_layer import QtBaseImageControls
from napari._qt.layers.qt_image_base_layer import (
QtBaseImageControls,
create_range_popup,
)
from napari.layers import Image, Surface

_IMAGE = np.arange(100).astype(np.uint16).reshape((10, 10))
Expand Down Expand Up @@ -72,3 +75,22 @@ def test_range_popup_clim_buttons(qtbot, layer):
assert tuple(qtctrl.contrastLimitsSlider.range()) == (0, 2 ** 16 - 1)
else:
assert rangebtn is None


@pytest.mark.parametrize('mag', [-12, -9, -3, 0, 2, 4, 6])
def test_clim_slider_step_size_and_precision(qtbot, mag):
"""Make sure the slider has a reasonable step size and precision.
...across a broad range of orders of magnitude.
"""
layer = Image(np.random.rand(20, 20) / 10 ** mag)
popup = create_range_popup(layer, 'contrast_limits')

# the range slider popup labels should have a number of decimal points that
# is inversely proportional to the order of magnitude of the range of data,
# but should never be greater than 5 or less than 0
assert popup.precision == max(min(mag + 3, 5), 0)

# the slider step size should also be inversely proportional to the data
# range, with 1000 steps across the data range
assert np.ceil(popup.slider._step * 10 ** (mag + 4)) == 10
8 changes: 7 additions & 1 deletion napari/_qt/qt_range_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ def __init__(

self.setRange((0, 100) if data_range is None else data_range)
self.setValues((20, 80) if initial_values is None else initial_values)
self.setStep(0.01 if step_size is None else step_size)
if step_size is None:
# pick an appropriate slider step size based on the data range
if data_range is not None:
step_size = (data_range[1] - data_range[0]) / 1000
else:
step_size = 0.001
self.setStep(step_size)
if not parent:
if 'HRange' in self.__class__.__name__:
self.setGeometry(200, 200, 200, 20)
Expand Down

0 comments on commit a10a88b

Please sign in to comment.