import math import platform import pytest from qtpy.QtCore import QEvent, QPoint, QPointF, Qt from qtpy.QtWidgets import QStyle, QStyleOptionSlider from superqt.sliders._generic_slider import _GenericSlider, _sliderValueFromPosition from ._testutil import ( _hover_event, _linspace, _mouse_event, _wheel_event, skip_on_linux_qt6, ) @pytest.fixture(params=[Qt.Orientation.Horizontal, Qt.Orientation.Vertical]) def gslider(qtbot, request): slider = _GenericSlider(request.param) qtbot.addWidget(slider) assert slider.value() == 0 assert slider.minimum() == 0 assert slider.maximum() == 99 yield slider slider.initStyleOption(QStyleOptionSlider()) def test_change_floatslider_range(gslider: _GenericSlider, qtbot): with qtbot.waitSignals([gslider.rangeChanged, gslider.valueChanged]): gslider.setMinimum(10) assert gslider.value() == 10 == gslider.minimum() assert gslider.maximum() == 99 with qtbot.waitSignal(gslider.rangeChanged): gslider.setMaximum(90) assert gslider.value() == 10 == gslider.minimum() assert gslider.maximum() == 90 with qtbot.waitSignals([gslider.rangeChanged, gslider.valueChanged]): gslider.setRange(20, 40) assert gslider.value() == 20 == gslider.minimum() assert gslider.maximum() == 40 with qtbot.waitSignal(gslider.valueChanged): gslider.setValue(30) assert gslider.value() == 30 with qtbot.waitSignals([gslider.rangeChanged, gslider.valueChanged]): gslider.setMaximum(25) assert gslider.value() == 25 == gslider.maximum() assert gslider.minimum() == 20 def test_float_values(gslider: _GenericSlider, qtbot): with qtbot.waitSignal(gslider.rangeChanged): gslider.setRange(0.25, 0.75) assert gslider.minimum() == 0.25 assert gslider.maximum() == 0.75 with qtbot.waitSignal(gslider.valueChanged): gslider.setValue(0.55) assert gslider.value() == 0.55 with qtbot.waitSignal(gslider.valueChanged): gslider.setValue(1.55) assert gslider.value() == 0.75 == gslider.maximum() def test_ticks(gslider: _GenericSlider, qtbot): gslider.setTickInterval(0.3) assert gslider.tickInterval() == 0.3 gslider.setTickPosition(gslider.TickPosition.TicksAbove) gslider.show() def test_show(gslider, qtbot): gslider.show() @pytest.mark.skipif(platform.system() != "Darwin", reason="cross-platform is tricky") def test_press_move_release(gslider: _GenericSlider, qtbot): # this fail on vertical came with pyside6.2 ... need to debug # still works in practice, but test fails to catch signals if gslider.orientation() == Qt.Orientation.Vertical: pytest.xfail() assert gslider._pressedControl == QStyle.SubControl.SC_None opt = QStyleOptionSlider() gslider.initStyleOption(opt) style = gslider.style() hrect = style.subControlRect( QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle ) handle_pos = gslider.mapToGlobal(hrect.center()) with qtbot.waitSignal(gslider.sliderPressed): qtbot.mousePress(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) assert gslider._pressedControl == QStyle.SubControl.SC_SliderHandle with qtbot.waitSignals([gslider.sliderMoved, gslider.valueChanged]): shift = ( QPoint(0, -8) if gslider.orientation() == Qt.Orientation.Vertical else QPoint(8, 0) ) gslider.mouseMoveEvent(_mouse_event(handle_pos + shift)) with qtbot.waitSignal(gslider.sliderReleased): qtbot.mouseRelease(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) assert gslider._pressedControl == QStyle.SubControl.SC_None gslider.show() with qtbot.waitSignal(gslider.sliderPressed): qtbot.mousePress(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) @skip_on_linux_qt6 def test_hover(gslider: _GenericSlider): # stub opt = QStyleOptionSlider() gslider.initStyleOption(opt) style = gslider.style() hrect = style.subControlRect( QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle ) handle_pos = QPointF(gslider.mapToGlobal(hrect.center())) assert gslider._hoverControl == QStyle.SubControl.SC_None gslider.event(_hover_event(QEvent.Type.HoverEnter, handle_pos, QPointF(), gslider)) assert gslider._hoverControl == QStyle.SubControl.SC_SliderHandle gslider.event( _hover_event(QEvent.Type.HoverLeave, QPointF(-1000, -1000), handle_pos, gslider) ) assert gslider._hoverControl == QStyle.SubControl.SC_None def test_wheel(gslider: _GenericSlider, qtbot): with qtbot.waitSignal(gslider.valueChanged): gslider.wheelEvent(_wheel_event(120)) gslider.wheelEvent(_wheel_event(0)) def test_position(gslider: _GenericSlider, qtbot): gslider.setSliderPosition(21.2) assert gslider.sliderPosition() == 21.2 def test_steps(gslider: _GenericSlider, qtbot): gslider.setSingleStep(0.1) assert gslider.singleStep() == 0.1 gslider.setSingleStep(1.5e20) assert gslider.singleStep() == 1.5e20 gslider.setPageStep(0.2) assert gslider.pageStep() == 0.2 gslider.setPageStep(1.5e30) assert gslider.pageStep() == 1.5e30 @pytest.mark.parametrize("mag", list(range(4, 37, 4)) + list(range(-4, -37, -4))) def test_slider_extremes(gslider: _GenericSlider, mag, qtbot): _mag = 10**mag with qtbot.waitSignal(gslider.rangeChanged): gslider.setRange(-_mag, _mag) for i in _linspace(-_mag, _mag, 10): gslider.setValue(i) assert math.isclose(gslider.value(), i, rel_tol=1e-8) gslider.initStyleOption(QStyleOptionSlider()) # args are (min: float, max: float, position: int, span: int, upsideDown: bool) @pytest.mark.parametrize( "args, result", [ # (min, max, pos, span[, inverted]), expectation # data range (1, 2) ((1, 2, 50, 100), 1.5), ((1, 2, 70, 100), 1.7), ((1, 2, 70, 100, True), 1.3), # inverted appearance ((1, 2, 170, 100), 2), ((1, 2, 100, 100), 2), ((1, 2, -30, 100), 1), # data range (-2, 2) ((-2, 2, 50, 100), 0), ((-2, 2, 75, 100), 1), ((-2, 2, 75, 100, True), -1), # inverted appearance ((-2, 2, 170, 100), 2), ((-2, 2, 100, 100), 2), ((-2, 2, -30, 100), -2), ], ) def test_slider_value_from_position(args, result): assert math.isclose(_sliderValueFromPosition(*args), result)