mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-13 10:36:35 +00:00
fix(scripts): scroll_speeds.py recommends keys the resolver honours
The ladder ended by telling users to set display_options.scroll_pixels_per_second. scroll_config ranks that key below the scroll_speed + scroll_delay pair, deliberately, and several plugin schemas default the pair into config, so the advised key was silently ignored (a schema-default 1/0.02 pair plus an advised 66 still resolved to 50 px/s). The advice is now the pair that selects the crisp speed exactly (pixels_per_frame every frame_hold/refresh seconds), explains that the pair outranks scroll_pixels_per_second, and gives the scoreboards' per-league scroll_settings.scroll_speed (px/s) form. Tests resolve the printed pair over a schema-default pair and check it lands on the advertised speed and hold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -190,9 +190,38 @@ def print_ladder(hz, highlight=None):
|
|||||||
) else ""
|
) else ""
|
||||||
print(" " + entry.describe() + mark)
|
print(" " + entry.describe() + mark)
|
||||||
print("")
|
print("")
|
||||||
print("Set one in config.json as pixels per second, e.g.")
|
print_config_advice(scroll_config.solve_crisp(highlight if highlight else hz / 2, hz))
|
||||||
print(' "display_options": {{"scroll_pixels_per_second": {:.0f}}}'.format(
|
|
||||||
scroll_config.solve_crisp(highlight if highlight else hz / 2, hz).pixels_per_second))
|
|
||||||
|
def config_advice(choice):
|
||||||
|
"""The config that selects ``choice``, in the keys the resolver honours.
|
||||||
|
|
||||||
|
Tickers take a ``scroll_speed`` (px per step) + ``scroll_delay`` (seconds)
|
||||||
|
pair, and scroll_config ranks that pair ABOVE ``scroll_pixels_per_second``
|
||||||
|
-- deliberately, because some plugins give the flat key a schema default.
|
||||||
|
Many schemas default the pair too, so a flat key added by hand is usually
|
||||||
|
ignored. Advise the pair: pixels_per_frame every frame_hold/refresh
|
||||||
|
seconds is exactly the crisp speed.
|
||||||
|
"""
|
||||||
|
pair = {
|
||||||
|
"scroll_speed": choice.pixels_per_frame,
|
||||||
|
"scroll_delay": round(choice.frame_hold / choice.refresh_hz, 6),
|
||||||
|
}
|
||||||
|
scoreboard = {"scroll_speed": round(choice.pixels_per_second, 2)}
|
||||||
|
return pair, scoreboard
|
||||||
|
|
||||||
|
|
||||||
|
def print_config_advice(choice):
|
||||||
|
pair, scoreboard = config_advice(choice)
|
||||||
|
print("To use {:.1f} px/s, set it where the plugin keeps its scroll speed.".format(
|
||||||
|
choice.pixels_per_second))
|
||||||
|
print("Tickers take a scroll_speed (px per step) + scroll_delay (seconds) pair:")
|
||||||
|
print(' "display_options": {}'.format(json.dumps(pair)))
|
||||||
|
print("(some plugins keep the pair at the top level or under \"display\").")
|
||||||
|
print("The pair outranks scroll_pixels_per_second, which is ignored whenever the")
|
||||||
|
print("pair is present -- and schema defaults usually put it there.")
|
||||||
|
print("Sports scoreboards take pixels per second per league instead:")
|
||||||
|
print(' "scroll_settings": {}'.format(json.dumps(scoreboard)))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -96,3 +96,49 @@ def test_the_service_uses_the_shared_builder(test_config):
|
|||||||
finally:
|
finally:
|
||||||
DisplayManager._instance = None
|
DisplayManager._instance = None
|
||||||
shared.assert_called_once()
|
shared.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
class TestSpeedAdvice:
|
||||||
|
"""The advice must name keys the resolver actually honours."""
|
||||||
|
|
||||||
|
def _advice(self, script, capsys, hz, want):
|
||||||
|
script.print_ladder(hz, want)
|
||||||
|
return capsys.readouterr().out
|
||||||
|
|
||||||
|
def test_advice_is_the_speed_delay_pair(self, script, capsys):
|
||||||
|
out = self._advice(script, capsys, 100.0, 60)
|
||||||
|
assert '"scroll_speed": 2' in out
|
||||||
|
assert '"scroll_delay": 0.03' in out
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("hz,want", [(100.0, 60), (100.0, 50), (120.0, 45),
|
||||||
|
(60.0, 30), (100.0, None)])
|
||||||
|
def test_the_advised_pair_resolves_to_the_advised_speed(self, script, capsys,
|
||||||
|
hz, want):
|
||||||
|
"""Apply the printed pair over a schema-default pair, as a saved config
|
||||||
|
would carry it, and the resolver must land on the advertised speed."""
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
from src.common import scroll_config
|
||||||
|
|
||||||
|
out = self._advice(script, capsys, hz, want)
|
||||||
|
block = re.search(r'"display_options": (\{[^}]*\})', out)
|
||||||
|
assert block, out
|
||||||
|
advised = json.loads(block.group(1))
|
||||||
|
config = {"display_options": {"scroll_speed": 1.0, "scroll_delay": 0.02,
|
||||||
|
"scroll_pixels_per_second": 999,
|
||||||
|
**advised}}
|
||||||
|
expected = scroll_config.solve_crisp(want if want else hz / 2, hz)
|
||||||
|
settings = scroll_config.configure(
|
||||||
|
_Helper(), plugin_config=config, refresh_hz=hz)
|
||||||
|
assert settings.pixels_per_second == pytest.approx(expected.pixels_per_second)
|
||||||
|
assert settings.frame_hold == expected.frame_hold
|
||||||
|
|
||||||
|
def test_the_deprecated_key_is_not_recommended(self, script, capsys):
|
||||||
|
out = self._advice(script, capsys, 100.0, 60)
|
||||||
|
assert '"scroll_pixels_per_second":' not in out
|
||||||
|
|
||||||
|
|
||||||
|
class _Helper:
|
||||||
|
def set_scroll_speed(self, speed):
|
||||||
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user