统一力值阈值处理并保留原始采样
移除自动显示基线,使逐轴阈值统一作用于曲线、读数与记录输出,标定拟合继续使用原始采样。 共享层全量测试和相关 Studio 测试通过。
This commit is contained in:
@@ -286,28 +286,20 @@ def test_a_partial_plot_settings_mapping_leaves_the_rest_as_stored():
|
||||
assert parts["charts"].deadband_floors[-1]["raw"] == 900.0
|
||||
|
||||
|
||||
def test_the_display_baseline_is_not_a_finger_only_setting():
|
||||
"""Every sensor type and both units: an idle newton offset is the same problem."""
|
||||
def test_force_threshold_policy_is_available_for_each_sensor_and_unit():
|
||||
for sensor, units in (("box", "newton"), ("box", "raw"),
|
||||
("finger", "newton"), ("finger", "raw")):
|
||||
controller, parts = _controller(prefs={"raw_auto_zero": True},
|
||||
sensor_type=sensor, units=units)
|
||||
controller, parts = _controller(sensor_type=sensor, units=units)
|
||||
assert parts["charts"].presentation_enabled[-1] is True, (sensor, units)
|
||||
assert parts["charts"].presentation[-1]["unit"] == (
|
||||
"N" if units == "newton" else "raw"
|
||||
)
|
||||
|
||||
controller, parts = _controller(prefs={"raw_auto_zero": False}, sensor_type="box",
|
||||
units="newton")
|
||||
assert parts["charts"].presentation_enabled[-1] is False
|
||||
|
||||
|
||||
def test_the_deadband_floors_reach_the_charts_from_the_preferences():
|
||||
controller, parts = _controller(prefs={"plot_deadband_n": 0.5, "plot_deadband_raw": 2000.0})
|
||||
assert parts["charts"].deadband_floors[-1] == {"newton": 0.5, "raw": 2000.0}
|
||||
|
||||
controller.set_raw_auto_zero(True) # re-applies the whole policy
|
||||
assert parts["charts"].deadband_floors[-1] == {"newton": 0.5, "raw": 2000.0}
|
||||
assert parts["charts"].presentation_enabled[-1] is True
|
||||
|
||||
|
||||
def test_the_sample_rate_prefers_the_measured_rate_over_the_stored_one():
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
"""The force charts as the operator sees them: baseline, deadband and ranges.
|
||||
|
||||
The display policy is a *view* concern: an idle channel must read zero and draw a
|
||||
flat line, while the session store and ``forces.csv`` keep the measurement. These
|
||||
tests drive the panel on its own (no acquisition, no window) and read back what
|
||||
the curves and the value cards were actually given.
|
||||
"""
|
||||
"""Force cards, curves, thresholds, precision and chart ranges."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
from orisys_app_shared.force.session import SessionSample, SessionStore
|
||||
from orisys_studio.ui.force_plots import ForcePlotsPanel, _nice_ticks, format_force
|
||||
|
||||
#: The two idle triplets from the customer's screenshots: a raw FN of ~9555 and
|
||||
#: an FX whose bias sits ~13k counts off zero.
|
||||
RAW_BIAS = np.array([9555.0, -13368.0, -353.0])
|
||||
NEWTON_BIAS = np.array([0.4, -0.25, 0.1])
|
||||
from orisys_studio.ui.force_plots import ForcePlotsPanel, _nice_ticks
|
||||
|
||||
|
||||
def _panel(unit: str = "raw") -> ForcePlotsPanel:
|
||||
"""A chart in one unit, with the display baseline switched on."""
|
||||
panel = ForcePlotsPanel(theme="dark")
|
||||
panel.set_units(unit)
|
||||
panel.configure_presentation(sensor_type="box", unit=unit)
|
||||
@@ -28,29 +15,6 @@ def _panel(unit: str = "raw") -> ForcePlotsPanel:
|
||||
return panel
|
||||
|
||||
|
||||
def _rest(
|
||||
panel: ForcePlotsPanel,
|
||||
bias: np.ndarray,
|
||||
*,
|
||||
unit: str = "raw",
|
||||
samples: int = 80,
|
||||
spread: float = 0.0,
|
||||
sigma: float = 0.0,
|
||||
seed: int = 0,
|
||||
) -> None:
|
||||
"""Feed a quiet startup window, the way a live session would.
|
||||
|
||||
``spread`` is a bounded wander (the device really does sit still) and
|
||||
``sigma`` is the random jitter of a `noisier` product.
|
||||
"""
|
||||
rng = np.random.default_rng(seed)
|
||||
for index in range(samples):
|
||||
sample = bias + ((index % 5) - 2) * spread
|
||||
if sigma:
|
||||
sample = sample + rng.normal(0.0, sigma, size=3)
|
||||
panel.push(index / 30.0, *sample, unit=unit)
|
||||
|
||||
|
||||
def _ticks(panel: ForcePlotsPanel, channel: int) -> list[float]:
|
||||
"""The Y tick values actually installed on one axis."""
|
||||
levels = panel.plots[channel].getAxis("left")._tickLevels
|
||||
@@ -63,174 +27,32 @@ def _curve(panel: ForcePlotsPanel, channel: int) -> np.ndarray:
|
||||
return np.asarray(values, dtype=float)
|
||||
|
||||
|
||||
# ------------------------------------------------------------ resting channel
|
||||
def test_the_cards_read_zero_from_the_first_samples_of_a_session(qt_app):
|
||||
"""The reported defect: FN 1118 raw, FX 34.16, FY -186.1 on the cards.
|
||||
|
||||
The screenshot was taken as the session started, so the aid has to be
|
||||
effective from the first samples — not once the whole quiet window closed.
|
||||
"""
|
||||
# ------------------------------------------------------------- force output
|
||||
def test_raw_threshold_and_precision_match_cards_and_curve(qt_app):
|
||||
panel = _panel("raw")
|
||||
for index in range(4):
|
||||
panel.push(index / 30.0, *RAW_BIAS, unit="raw")
|
||||
if index < 2:
|
||||
# Nothing has been estimated from fewer than three samples yet; those
|
||||
# first frames honestly show the measurement.
|
||||
continue
|
||||
assert [label.text() for label in panel.value_labels] == ["0"] * 3, index
|
||||
|
||||
assert not panel.presentation.ready, "the whole three second window was needed"
|
||||
assert panel.presentation.display_quality == "provisional"
|
||||
for channel in (0, 1, 2):
|
||||
assert set(np.unique(_curve(panel, channel))) == {0.0}, channel
|
||||
panel.set_deadband_floors(raw=50.0)
|
||||
panel.push(0.0, 49.0, -50.0, 51.2, unit="raw")
|
||||
assert [label.text() for label in panel.value_labels] == ["0", "0", "51"]
|
||||
assert [_curve(panel, axis)[-1] for axis in range(3)] == [0.0, 0.0, 51.2]
|
||||
panel.set_value_decimals(raw=1, newton=2)
|
||||
assert panel.value_labels[2].text() == "51.2"
|
||||
|
||||
|
||||
def test_the_newton_cards_read_zero_from_the_first_samples_too(qt_app):
|
||||
"""Both units: a newton offset at rest is the same display problem."""
|
||||
def test_newton_threshold_and_precision_match_cards_and_curve(qt_app):
|
||||
panel = _panel("N")
|
||||
for index in range(4):
|
||||
panel.push(index / 30.0, *NEWTON_BIAS, unit="N")
|
||||
if index < 2:
|
||||
continue
|
||||
assert [label.text() for label in panel.value_labels] == ["0.000"] * 3, index
|
||||
assert not panel.presentation.ready
|
||||
panel.set_deadband_floors(newton=0.5)
|
||||
panel.push(0.0, 0.49, -0.5, 0.51, unit="N")
|
||||
assert [label.text() for label in panel.value_labels] == ["0.00", "0.00", "0.51"]
|
||||
assert [_curve(panel, axis)[-1] for axis in range(3)] == [0.0, 0.0, 0.51]
|
||||
|
||||
|
||||
def test_a_card_and_its_own_curve_are_the_same_value(qt_app):
|
||||
"""A card may never read a number its curve does not carry.
|
||||
|
||||
The extra "the estimate is ready" gate in ``_render_value_cards`` is what
|
||||
used to let the card show the raw measurement while the curve was already
|
||||
drawn through the applied estimate.
|
||||
"""
|
||||
def test_threshold_has_no_estimated_baseline(qt_app):
|
||||
panel = _panel("raw")
|
||||
for index in range(4):
|
||||
panel.push(index / 30.0, *RAW_BIAS, unit="raw")
|
||||
# A real contact, flagged as one so it does not enter the idle estimate.
|
||||
panel.push(1.0, *(RAW_BIAS + [4000.0, 0.0, 0.0]), unit="raw", contact_found=True)
|
||||
|
||||
for channel in range(3):
|
||||
drawn = float(_curve(panel, channel)[-1])
|
||||
assert panel.value_labels[channel].text() == format_force(
|
||||
drawn, "raw", presentation=panel.presentation
|
||||
), channel
|
||||
assert panel.value_labels[0].text() not in ("0", "1118"), "the contact was lost"
|
||||
|
||||
|
||||
def test_a_resting_raw_channel_reads_zero_and_draws_a_flat_curve(qt_app):
|
||||
"""FN 9555 / FX -13368 at rest must read 0 and draw a line on zero."""
|
||||
panel = _panel("raw")
|
||||
_rest(panel, RAW_BIAS, spread=20.0)
|
||||
|
||||
assert panel.presentation.ready, "the quiet window never closed"
|
||||
for index, label in enumerate(panel.value_labels):
|
||||
assert label.text() == "0", (index, label.text())
|
||||
for channel in (0, 1, 2):
|
||||
values = _curve(panel, channel)
|
||||
assert values.size, channel
|
||||
assert set(np.unique(values)) == {0.0}, (channel, np.unique(values))
|
||||
|
||||
|
||||
def test_a_resting_newton_channel_is_baselined_too(qt_app):
|
||||
"""The same aid in newtons: it is a display problem in either unit."""
|
||||
panel = _panel("N")
|
||||
_rest(panel, NEWTON_BIAS, unit="N")
|
||||
|
||||
assert panel.presentation.ready
|
||||
assert [label.text() for label in panel.value_labels] == ["0.000"] * 3
|
||||
assert set(np.unique(_curve(panel, 0))) == {0.0}
|
||||
|
||||
|
||||
def test_noise_outside_the_estimated_band_is_still_rare(qt_app):
|
||||
"""A p99 band, so a resting *noisy* channel is flat but not rigid.
|
||||
|
||||
The offset removal is what fixes a 13k-count bias; the percentile band holds
|
||||
the remaining jitter at zero. A few idle samples may sit just outside it —
|
||||
that is what the operator's deadband floor in the dialog is for.
|
||||
"""
|
||||
panel = _panel("raw")
|
||||
_rest(panel, RAW_BIAS, sigma=30.0)
|
||||
|
||||
assert panel.presentation.ready
|
||||
values = _curve(panel, 1)
|
||||
zeroed = float(np.mean(values == 0.0))
|
||||
assert zeroed >= 0.95, f"only {zeroed:.2%} of a resting channel was zeroed"
|
||||
assert abs(float(np.median(values))) < 1.0
|
||||
|
||||
|
||||
def test_the_record_keeps_the_measurement_while_the_display_is_zeroed(qt_app):
|
||||
"""The deadband is a display copy: the buffer and the store see the truth."""
|
||||
panel = _panel("raw")
|
||||
store = SessionStore()
|
||||
panel.set_store(store)
|
||||
for index in range(6):
|
||||
panel.push(index / 30.0, *RAW_BIAS, unit="raw")
|
||||
store.append(
|
||||
SessionSample(
|
||||
index,
|
||||
index / 30.0,
|
||||
fn_raw=RAW_BIAS[0],
|
||||
fx_raw=RAW_BIAS[1],
|
||||
fy_raw=RAW_BIAS[2],
|
||||
fn_n=NEWTON_BIAS[0],
|
||||
fx_n=NEWTON_BIAS[1],
|
||||
fy_n=NEWTON_BIAS[2],
|
||||
valid=True,
|
||||
)
|
||||
)
|
||||
|
||||
# The very values the customer complained about are still measured, from the
|
||||
# first sample on — in the live buffer...
|
||||
state = panel.history.state()
|
||||
assert state is not None
|
||||
assert abs(float(np.median(state.values[:, 0])) - RAW_BIAS[0]) < 100.0
|
||||
assert abs(float(np.median(state.values[:, 1])) - RAW_BIAS[1]) < 100.0
|
||||
# ...and in the session store (which is what forces.csv is written from).
|
||||
stored = store.data().raw
|
||||
assert stored.shape[0] == 6
|
||||
assert float(np.median(stored[:, 0])) == RAW_BIAS[0]
|
||||
assert float(np.median(stored[:, 1])) == RAW_BIAS[1]
|
||||
assert float(np.median(stored[:, 2])) == RAW_BIAS[2]
|
||||
|
||||
# ...while the cards and the curve drawn from it are already flat on zero.
|
||||
assert [label.text() for label in panel.value_labels] == ["0"] * 3
|
||||
assert set(np.unique(_curve(panel, 1))) == {0.0}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ deadband
|
||||
def test_a_small_excursion_is_suppressed_and_a_larger_one_is_not(qt_app):
|
||||
panel = _panel("raw")
|
||||
_rest(panel, RAW_BIAS, spread=20.0)
|
||||
estimate = panel.presentation.last_estimate
|
||||
assert estimate is not None and estimate.quality == "ok"
|
||||
deadband = float(estimate.deadbands[0])
|
||||
assert deadband > 0.0
|
||||
|
||||
panel.push(3.0, *(RAW_BIAS + [0.5 * deadband, 0.0, 0.0]), unit="raw")
|
||||
assert panel.value_labels[0].text() == "0", "half the deadband was drawn"
|
||||
|
||||
panel.push(3.1, *(RAW_BIAS + [3.0 * deadband, 0.0, 0.0]), unit="raw")
|
||||
assert panel.value_labels[0].text() != "0", (
|
||||
"an excursion well outside the deadband was swallowed"
|
||||
)
|
||||
assert abs(_curve(panel, 0)[-1]) > 2.0 * deadband
|
||||
# ...and the other axes, which stayed quiet, are still zero.
|
||||
assert panel.value_labels[1].text() == "0"
|
||||
|
||||
|
||||
def test_the_operator_floor_widens_the_deadband_at_once(qt_app):
|
||||
panel = _panel("raw")
|
||||
_rest(panel, RAW_BIAS, spread=20.0)
|
||||
|
||||
panel.set_deadband_floors(raw=250000.0)
|
||||
panel.push(3.0, *(RAW_BIAS + [50000.0, 0.0, 0.0]), unit="raw")
|
||||
assert panel.value_labels[0].text() == "0", (
|
||||
"an excursion inside the operator's floor was drawn"
|
||||
)
|
||||
|
||||
panel.set_deadband_floors(raw=0.0) # "auto": the layer's own floor returns
|
||||
assert panel.presentation.min_deadband_for("raw") == 50.0
|
||||
|
||||
panel.set_deadband_floors(raw=50.0)
|
||||
panel.push(0.0, 9555.0, -13368.0, 20.0, unit="raw")
|
||||
assert [label.text() for label in panel.value_labels] == ["9555", "-13368", "0"]
|
||||
panel.set_deadband_floors(raw=0.0)
|
||||
assert panel.presentation.min_deadband_for("raw") == 0.0
|
||||
|
||||
# -------------------------------------------------------------------- ranges
|
||||
def test_the_configured_ranges_reach_every_axis(qt_app):
|
||||
|
||||
@@ -634,6 +634,8 @@ def test_unit_switch_rebuilds_from_the_session_store(make_window):
|
||||
import math
|
||||
|
||||
window, stuck = _running_window(make_window)
|
||||
window.recording.set_force_thresholds(raw=0.0, newton=0.0)
|
||||
window.force_plots.set_deadband_floors(raw=0.0, newton=0.0)
|
||||
window.toolbar.units_combo.setCurrentIndex(1) # raw
|
||||
window.force_plots.reset()
|
||||
for index in range(60):
|
||||
@@ -1700,7 +1702,7 @@ def test_the_replay_view_loads_a_recording_and_scrubs_it(make_window, replay_rec
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
panel = window.force_plots
|
||||
assert panel.is_recording_source(), "the charts did not switch to the recording"
|
||||
@@ -1787,15 +1789,12 @@ def test_a_late_calibration_does_not_plot_raw_counts_on_the_newton_axis(make_win
|
||||
|
||||
# The sample that caused the switch still reaches the readouts; the early
|
||||
# return used to leave the cards showing the previous sample. The number on
|
||||
# the card is the *display* copy of it: this fake FN is a slow 2.0…2.4 N ramp
|
||||
# with no contact flagged, which is exactly the resting drift the display
|
||||
# baseline holds at zero. What matters here is that the card is not a raw
|
||||
# count, not the previous sample and not a dash.
|
||||
# the card uses the newton sample, with only the configured absolute threshold.
|
||||
assert abs(plots._last_values[0] - (2.0 + 39 * 0.01)) < 1e-9
|
||||
assert plots.presentation.display_quality != "none", (
|
||||
assert plots.presentation.display_quality == "threshold", (
|
||||
"the display policy was not applied to the freshly shown newton unit"
|
||||
)
|
||||
assert plots.value_labels[0].text() == "0.000", plots.value_labels[0].text()
|
||||
assert plots.value_labels[0].text() == "2.39", plots.value_labels[0].text()
|
||||
assert plots.value_labels[0].text() != t("chart.value_unavailable")
|
||||
_finish(window, stuck)
|
||||
|
||||
@@ -1940,9 +1939,9 @@ def test_a_replay_keeps_the_2d_pane_from_the_live_camera(
|
||||
window, stuck = _running_window(make_window)
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording), "the replay could not open it"
|
||||
assert window.replay_ctl.load_recording(replay_recording), "the replay could not open it"
|
||||
QApplication.processEvents()
|
||||
assert window._replay_owns_image(), "the replay does not own the 2D pane"
|
||||
assert window.replay_ctl.owns_image(), "the replay does not own the 2D pane"
|
||||
|
||||
label = window.arrow_panel.image_label
|
||||
# Park the playhead at the end of the recording and remember what the pane and
|
||||
@@ -1971,7 +1970,7 @@ def test_a_replay_keeps_the_2d_pane_from_the_live_camera(
|
||||
# meaningful rather than a dead pane.
|
||||
window.brand_bar.btn_live.click()
|
||||
QApplication.processEvents()
|
||||
assert not window._replay_owns_image()
|
||||
assert not window.replay_ctl.owns_image()
|
||||
stuck.emit(ui_check._fake_packet(9, mono_t=9 / 30.0, frame=live))
|
||||
window._on_run_tick()
|
||||
QApplication.processEvents()
|
||||
@@ -1993,7 +1992,7 @@ def test_the_readout_describes_the_point_the_cursor_is_on(
|
||||
window = _show(make_window(), (1440, 880))
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
plots = window.force_plots
|
||||
assert plots.mode() == "browse", "opening a recording did not browse it"
|
||||
@@ -2028,7 +2027,7 @@ def test_a_raw_only_recording_is_never_shown_in_newton(make_window, replay_recor
|
||||
QApplication.processEvents()
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
|
||||
panel = window.force_plots
|
||||
@@ -2051,7 +2050,7 @@ def test_the_replay_transport_plays_and_pauses(make_window, replay_recording):
|
||||
window = _show(make_window(), (1440, 880))
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
panel = window.force_plots
|
||||
|
||||
@@ -2091,7 +2090,7 @@ def test_the_recompute_switch_asks_for_analysis_not_an_acquire_chain(
|
||||
window = _show(make_window(), (1440, 880))
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
panel = window.force_plots
|
||||
assert panel.is_recording_source(), "the charts do not read the recording"
|
||||
@@ -2140,7 +2139,7 @@ def test_the_replay_view_offers_a_stop_that_always_works(make_window, replay_rec
|
||||
window = _show(make_window(), (1440, 880))
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
|
||||
assert window.toolbar.btn_reset.isEnabled(), (
|
||||
@@ -2197,7 +2196,7 @@ def test_escape_stops_the_replay_too(make_window, replay_recording):
|
||||
window = _show(make_window(), (1440, 880))
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
window.toolbar.btn_run.setChecked(True)
|
||||
QApplication.processEvents()
|
||||
@@ -2252,7 +2251,7 @@ def test_the_replay_reset_is_the_display_reset_and_never_the_device(
|
||||
window._finger_worker = stuck
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_recording(replay_recording)
|
||||
assert window.replay_ctl.load_recording(replay_recording)
|
||||
QApplication.processEvents()
|
||||
|
||||
assert window.toolbar.reset_target() == "display"
|
||||
@@ -2298,9 +2297,8 @@ def test_the_replay_reset_is_the_display_reset_and_never_the_device(
|
||||
window.close()
|
||||
|
||||
|
||||
def test_the_live_reset_zeroes_the_device_and_restarts_the_display_baseline(make_window):
|
||||
"""The live half of the same control: the device is asked, and the tip's side
|
||||
effect (the display baseline restarting) really happens."""
|
||||
def test_the_live_reset_zeroes_the_device_without_changing_threshold(make_window):
|
||||
"""The device reset must not erase or re-estimate the configured threshold."""
|
||||
from orisys_app_shared.i18n import t
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
@@ -2322,14 +2320,11 @@ def test_the_live_reset_zeroes_the_device_and_restarts_the_display_baseline(make
|
||||
assert window.toolbar.btn_reset.isEnabled()
|
||||
assert window.toolbar.btn_reset.text() == t("toolbar.reset_sensor")
|
||||
assert window.toolbar.btn_reset.toolTip() == t("toolbar.reset_sensor_tip")
|
||||
restarts: list = []
|
||||
window.force_plots.request_presentation_reestimate = lambda: restarts.append(True)
|
||||
threshold = window.force_plots.presentation.min_deadband_for("raw")
|
||||
|
||||
window.toolbar.btn_reset.click()
|
||||
assert stuck.reset_requests == 1, "the live reset did not reach the device"
|
||||
assert restarts == [True], (
|
||||
"the display baseline was not restarted with the zero (the tooltip's side effect)"
|
||||
)
|
||||
assert window.force_plots.presentation.min_deadband_for("raw") == threshold
|
||||
assert window.statusBar().currentMessage() == t("status.reset_pending"), (
|
||||
window.statusBar().currentMessage()
|
||||
)
|
||||
@@ -2460,7 +2455,7 @@ def test_a_bare_video_plays_even_though_it_is_not_a_recording(
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert not window.replay.has_video()
|
||||
assert window._load_replay_video(bare) is True, "a playable video was refused"
|
||||
assert window.replay_ctl.load_video(bare) is True, "a playable video was refused"
|
||||
QApplication.processEvents()
|
||||
assert window.replay.has_video(), "nothing is playable"
|
||||
assert window.replay.is_video_only(), "a bare video was taken for a recording"
|
||||
@@ -2534,8 +2529,8 @@ def test_the_replay_and_the_live_paths_share_one_image_backend(
|
||||
app_module.render_image = spy
|
||||
try:
|
||||
window.layout_ctl.apply_preset("replay")
|
||||
assert window._load_replay_video(bare) is True
|
||||
window._render_replay_frame()
|
||||
assert window.replay_ctl.load_video(bare) is True
|
||||
window.replay_ctl.render_frame()
|
||||
QApplication.processEvents()
|
||||
assert window.arrow_panel.plan_modes() == ("recorded",), (
|
||||
"a recording must not offer a derived view"
|
||||
@@ -2623,7 +2618,7 @@ def test_the_replay_entries_exist_only_in_the_replay_view(
|
||||
assert not window.toolbar.btn_recompute.isEnabled()
|
||||
|
||||
# Programmatic use is refused too, and leaves nothing half-changed.
|
||||
assert window._load_replay_video(bare) is False
|
||||
assert window.replay_ctl.load_video(bare) is False
|
||||
QApplication.processEvents()
|
||||
assert not window.replay.has_video()
|
||||
assert window.toolbar.source_mode() == "camera"
|
||||
@@ -2637,7 +2632,7 @@ def test_the_replay_entries_exist_only_in_the_replay_view(
|
||||
QApplication.processEvents()
|
||||
for action in (window.act_open_video, window.act_playback, window.act_playback_latest):
|
||||
assert action.isEnabled()
|
||||
assert window._load_replay_video(bare) is True
|
||||
assert window.replay_ctl.load_video(bare) is True
|
||||
QApplication.processEvents()
|
||||
assert window.arrow_panel.plan_modes() == ("recorded",)
|
||||
assert window.toolbar.btn_recompute.isEnabled(), (
|
||||
@@ -2670,7 +2665,7 @@ def test_a_file_that_is_not_a_video_at_all_is_refused_with_a_reason(
|
||||
try:
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
assert window._load_replay_video(broken) is False, (
|
||||
assert window.replay_ctl.load_video(broken) is False, (
|
||||
"a file that cannot be decoded was accepted for playback"
|
||||
)
|
||||
QApplication.processEvents()
|
||||
@@ -2700,7 +2695,7 @@ def test_a_video_inside_a_recording_opens_the_recording_and_plays(
|
||||
QApplication.processEvents()
|
||||
segments = sorted(Path(replay_recording).glob("raw_*.mp4"))
|
||||
assert segments, "the fixture wrote no video segment"
|
||||
assert window._load_replay_video(segments[0]) is True
|
||||
assert window.replay_ctl.load_video(segments[0]) is True
|
||||
QApplication.processEvents()
|
||||
assert window.replay.is_loaded()
|
||||
assert window.toolbar.btn_run.isEnabled(), "the transport is not offered"
|
||||
@@ -2736,11 +2731,11 @@ def test_a_recording_without_a_video_says_it_cannot_be_played(make_window, recor
|
||||
window = _show(make_window(), (1440, 880))
|
||||
window.brand_bar.btn_playback.click()
|
||||
QApplication.processEvents()
|
||||
if not window._load_recording(directory):
|
||||
if not window.replay_ctl.load_recording(directory):
|
||||
pytest.skip("this recording layout was refused before it could be played")
|
||||
QApplication.processEvents()
|
||||
assert window.replay.duration_s() <= 0.0
|
||||
window._start_playback()
|
||||
window.replay_ctl.start_playback()
|
||||
QApplication.processEvents()
|
||||
assert not window._play_timer.isActive()
|
||||
assert t("playback.empty_recording")[:8] in window.statusBar().currentMessage(), (
|
||||
@@ -2784,7 +2779,7 @@ def test_a_selection_moves_its_statistics_onto_the_value_cards(make_window):
|
||||
), summary
|
||||
# The numbers a reader cannot fit in the strip are on the cards instead.
|
||||
expected_peak = float(plots._selection.peaks[0])
|
||||
expected = f"{expected_peak:.3f}"
|
||||
expected = f"{expected_peak:.2f}"
|
||||
assert plots.value_labels[0].text() == expected, (
|
||||
f"the FN card is not the selection's peak: {plots.value_labels[0].text()!r}"
|
||||
)
|
||||
@@ -3074,9 +3069,9 @@ def test_the_plot_dialog_carries_the_ranges_and_the_zero_threshold(qt_app):
|
||||
assert raw.settings()["fn_raw_min"] == 0.0
|
||||
assert raw.settings()["fn_min"] == 0.0
|
||||
assert raw.settings()["window_sec"] == 4.0
|
||||
assert raw.deadbands() == {"deadband_n": 0.0, "deadband_raw": 0.0}, (
|
||||
"the shipped deadband answer is 'auto'"
|
||||
)
|
||||
assert raw.deadbands() == {"deadband_n": 0.0, "deadband_raw": 50.0}
|
||||
assert raw.settings()["newton_decimals"] == 2
|
||||
assert raw.settings()["raw_decimals"] == 0
|
||||
|
||||
|
||||
def test_the_plot_dialog_values_reach_the_chart(make_window):
|
||||
@@ -3113,12 +3108,11 @@ def test_the_plot_dialog_values_reach_the_chart(make_window):
|
||||
window.close()
|
||||
|
||||
|
||||
def test_the_display_baseline_is_on_for_every_sensor_by_default(make_window):
|
||||
"""It used to be Finger-raw only; an idle box sensor drifts just as much."""
|
||||
def test_force_threshold_is_on_for_every_sensor_by_default(make_window):
|
||||
window, stuck = _running_window(make_window) # sensor_type="box"
|
||||
|
||||
plots = window.force_plots
|
||||
assert plots._presentation_enabled is True, "a box sensor lost its baseline"
|
||||
assert plots._presentation_enabled is True
|
||||
assert plots.presentation.enabled is True
|
||||
# The per-unit floors came from the preferences, not from a sensor type.
|
||||
assert plots.presentation.min_deadband_for("raw") == 50.0
|
||||
|
||||
Reference in New Issue
Block a user