v0.45.1: the laser's Afterimage is a number box, like Line width

Type the seconds, or step by 0.05 with the arrows or ↑/↓; it reads "Off"
at zero. Line width and Afterimage now share one number-box helper.
This commit is contained in:
2026-10-10 02:24:17 -07:00
parent 43d2d21d9d
commit c312751c86
4 changed files with 40 additions and 18 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
"""LinTunes — iTunes-style music library manager and player for Linux."""
__version__ = "0.45.0"
__version__ = "0.45.1"
+20 -14
View File
@@ -507,16 +507,18 @@ class PreferencesDialog(QDialog):
laser = QWidget()
form = self._viz_form(laser)
self._preset_row(form, "laser")
self._laser_fade_slider, _ = self._viz_slider(
self._laser_fade_box = self._viz_spinbox(
form, "Afterimage:", "visualizer_laser_afterimage", config,
scale=20, fmt=_fade_text,
step=0.05, decimals=2, suffix=" s", off_text="Off",
tip="How long past sweeps of the beam take to fade — 0 is none")
self._speaker = self._speaker_range(form, config)
# The look controls on the left, and beside them a drawing of the
# rig the settings describe — it labels the distance, the angles
# and the dot, so those sliders carry no number of their own.
looks = self._viz_form(None)
self._width_box = self._width_spinbox(looks, config)
self._width_box = self._viz_spinbox(
looks, "Line width:", "visualizer_laser_width", config,
step=0.5, decimals=1, suffix=" px")
self._glow = self._viz_check(
looks, "Glow", "visualizer_laser_glow", config,
tip="The haze around the beam")
@@ -583,27 +585,31 @@ class PreferencesDialog(QDialog):
QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
return form
def _width_spinbox(self, form, config) -> QDoubleSpinBox:
"""Line width as a number: type it, or step it by half a pixel with
the arrows (buttons or keys)."""
lo, hi = visualizer_styles.LIMITS["visualizer_laser_width"]
def _viz_spinbox(self, form, label, key, config, *, step, decimals,
suffix, off_text="", tip="") -> QDoubleSpinBox:
"""A setting as a number: type it, or step it with the arrows
(buttons or ↑/↓). ``off_text`` is shown in place of the minimum."""
lo, hi = visualizer_styles.LIMITS[key]
box = QDoubleSpinBox()
box.setRange(lo, hi)
box.setSingleStep(0.5)
box.setDecimals(1)
box.setSuffix(" px")
box.setValue(config["visualizer_laser_width"]) # before connect
box.setSingleStep(step)
box.setDecimals(decimals)
box.setSuffix(suffix)
box.setToolTip(tip)
if off_text:
box.setSpecialValueText(off_text)
box.setValue(config[key]) # before connect
def changed(value):
self._prefs.set_live("visualizer_laser_width", value)
self._prefs.set_live(key, value)
self._color_save_timer.start()
box.valueChanged.connect(changed)
row = QHBoxLayout()
row.addWidget(box)
row.addStretch()
form.addRow("Line width:", row)
self._viz_setters["visualizer_laser_width"] = box.setValue
form.addRow(label, row)
self._viz_setters[key] = box.setValue
return box
# ---- Visualizer presets ----
+6 -3
View File
@@ -614,12 +614,15 @@ class TestPreferencesPage:
prefs = Preferences(tmp_path)
dialog = self._dialog(prefs)
for slider, key in ((dialog._bars_fade_slider, "visualizer_bars_fade"),
(dialog._fade_slider, "visualizer_ghost_fade"),
(dialog._laser_fade_slider,
"visualizer_laser_afterimage")):
(dialog._fade_slider, "visualizer_ghost_fade")):
assert slider.minimum() == 0 # 0 = off
slider.setValue(10)
assert prefs.get(key) == 0.5
# The laser's is a number box, like Line width.
box = dialog._laser_fade_box
assert box.minimum() == 0
box.setValue(0.5)
assert prefs.get("visualizer_laser_afterimage") == 0.5
def test_laser_settings(self, qapp, tmp_path):
prefs = Preferences(tmp_path)
+13
View File
@@ -282,3 +282,16 @@ class TestCopyAsText:
from lintunes.gui.track_table import song_line
from lintunes.models import Track
assert song_line(Track(track_id=9, name="", artist="X")) == "X - Untitled"
class TestAfterimageBox:
def test_steps_by_a_twentieth_and_reads_off_at_zero(self, qapp, tmp_path):
prefs = Preferences(tmp_path)
box = _prefs_dialog(prefs)._laser_fade_box
assert box.value() == pytest.approx(0.1) # the default
box.stepUp()
assert prefs.get("visualizer_laser_afterimage") == pytest.approx(0.15)
box.setValue(0)
assert box.text() == "Off"
box.setValue(99) # past the top
assert box.value() == 3.0