|
36 | 36 | ################################################## |
37 | 37 |
|
38 | 38 |
|
| 39 | +def make_alignment_fit_summary(center: float | None = None) -> dict: |
| 40 | + params = [] |
| 41 | + if center is not None: |
| 42 | + params.append(["center", center, True, None, -np.inf, np.inf, None, 0.1, {}, 0.0, None]) |
| 43 | + params.append(["sigma", 0.5, True, None, 0.0, np.inf, None, 0.1, {}, 1.0, None]) |
| 44 | + return { |
| 45 | + "model": "Model(test)", |
| 46 | + "method": "leastsq", |
| 47 | + "chisqr": 1.0, |
| 48 | + "redchi": 1.0, |
| 49 | + "rsquared": 0.99, |
| 50 | + "message": "Fit succeeded.", |
| 51 | + "params": params, |
| 52 | + } |
| 53 | + |
| 54 | + |
39 | 55 | def test_waveform_initialization(qtbot, mocked_client): |
40 | 56 | """ |
41 | 57 | Test that a new Waveform widget initializes with the correct defaults. |
@@ -496,6 +512,183 @@ def test_add_dap_curve_custom_source(qtbot, mocked_client_with_dap): |
496 | 512 | assert dap_curve.config.signal.dap == "GaussianModel" |
497 | 513 |
|
498 | 514 |
|
| 515 | +def test_alignment_mode_toggle_shows_bottom_panel(qtbot, mocked_client): |
| 516 | + wf = create_widget(qtbot, Waveform, client=mocked_client) |
| 517 | + |
| 518 | + action = wf.toolbar.components.get_action("alignment_mode").action |
| 519 | + action.trigger() |
| 520 | + |
| 521 | + assert wf._alignment_panel_visible is True |
| 522 | + assert wf._alignment_side_panel.panel_visible is True |
| 523 | + assert action.isChecked() is True |
| 524 | + |
| 525 | + action.trigger() |
| 526 | + |
| 527 | + assert wf._alignment_panel_visible is False |
| 528 | + assert wf._alignment_side_panel.panel_visible is False |
| 529 | + assert action.isChecked() is False |
| 530 | + |
| 531 | + |
| 532 | +def test_resolve_alignment_positioner(qtbot, mocked_client): |
| 533 | + wf = create_widget(qtbot, Waveform, client=mocked_client) |
| 534 | + |
| 535 | + wf.x_mode = "samx" |
| 536 | + assert wf._resolve_alignment_positioner() == "samx" |
| 537 | + |
| 538 | + wf.x_mode = "auto" |
| 539 | + wf._current_x_device = ("samx", "samx") |
| 540 | + assert wf._resolve_alignment_positioner() == "samx" |
| 541 | + |
| 542 | + wf._current_x_device = ("bpm4i", "bpm4i") |
| 543 | + assert wf._resolve_alignment_positioner() is None |
| 544 | + |
| 545 | + wf.x_mode = "index" |
| 546 | + assert wf._resolve_alignment_positioner() is None |
| 547 | + |
| 548 | + wf.x_mode = "timestamp" |
| 549 | + assert wf._resolve_alignment_positioner() is None |
| 550 | + |
| 551 | + |
| 552 | +def test_alignment_panel_updates_when_auto_x_motor_changes( |
| 553 | + qtbot, mocked_client_with_dap, monkeypatch |
| 554 | +): |
| 555 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 556 | + wf.plot(arg1="bpm4i", dap="GaussianModel") |
| 557 | + wf.x_mode = "auto" |
| 558 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 559 | + |
| 560 | + wf._current_x_device = ("samx", "samx") |
| 561 | + wf._alignment_panel.set_positioner_device("samx") |
| 562 | + wf.scan_item = create_dummy_scan_item() |
| 563 | + wf.scan_item.metadata["bec"]["scan_report_devices"] = ["samy"] |
| 564 | + |
| 565 | + data = { |
| 566 | + "samy": {"samy": {"val": np.array([1.0, 2.0, 3.0])}}, |
| 567 | + "bpm4i": {"bpm4i": {"val": np.array([10.0, 20.0, 30.0])}}, |
| 568 | + } |
| 569 | + monkeypatch.setattr(wf, "_fetch_scan_data_and_access", lambda: (data, "val")) |
| 570 | + |
| 571 | + wf._get_x_data("bpm4i", "bpm4i") |
| 572 | + |
| 573 | + assert wf._current_x_device == ("samy", "samy") |
| 574 | + assert wf._alignment_positioner_name == "samy" |
| 575 | + assert wf._alignment_panel.positioner.device == "samy" |
| 576 | + |
| 577 | + |
| 578 | +def test_alignment_panel_disables_without_positioner(qtbot, mocked_client_with_dap): |
| 579 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 580 | + wf.plot(arg1="bpm4i") |
| 581 | + wf.x_mode = "index" |
| 582 | + |
| 583 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 584 | + |
| 585 | + assert wf._alignment_panel.positioner.isEnabled() is False |
| 586 | + assert "positioner on the x axis" in wf._alignment_panel.status_label.text() |
| 587 | + |
| 588 | + |
| 589 | +def test_alignment_marker_updates_from_positioner_readback(qtbot, mocked_client_with_dap): |
| 590 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 591 | + wf.plot(arg1="bpm4i", dap="GaussianModel") |
| 592 | + wf.x_mode = "samx" |
| 593 | + |
| 594 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 595 | + wf.dev["samx"].signals["samx"]["value"] = 4.2 |
| 596 | + wf._alignment_panel.positioner.force_update_readback() |
| 597 | + |
| 598 | + assert wf._alignment_marker_line is not None |
| 599 | + assert np.isclose(wf._alignment_marker_line.value(), 4.2) |
| 600 | + assert "samx" in wf._alignment_marker_line.label.toPlainText() |
| 601 | + assert "4.200" in wf._alignment_marker_line.label.toPlainText() |
| 602 | + |
| 603 | + |
| 604 | +def test_alignment_panel_uses_existing_dap_curves_and_moves_positioner( |
| 605 | + qtbot, mocked_client_with_dap |
| 606 | +): |
| 607 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 608 | + source_curve = wf.plot(arg1="bpm4i") |
| 609 | + dap_curve = wf.add_dap_curve(device_label=source_curve.name(), dap_name="GaussianModel") |
| 610 | + wf.x_mode = "samx" |
| 611 | + |
| 612 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 613 | + fit_summary = make_alignment_fit_summary(center=2.5) |
| 614 | + wf.dap_summary_update.emit(fit_summary, {"curve_id": dap_curve.name()}) |
| 615 | + wf._alignment_panel.fit_dialog.select_curve(dap_curve.name()) |
| 616 | + |
| 617 | + move_spy = MagicMock() |
| 618 | + wf.dev["samx"].move = move_spy |
| 619 | + |
| 620 | + assert wf._alignment_panel.fit_dialog.fit_curve_id == dap_curve.name() |
| 621 | + assert wf._alignment_panel.fit_dialog.action_buttons["center"].isEnabled() is True |
| 622 | + |
| 623 | + wf._alignment_panel.fit_dialog.action_buttons["center"].click() |
| 624 | + |
| 625 | + move_spy.assert_called_once_with(2.5, relative=False) |
| 626 | + |
| 627 | + |
| 628 | +def test_alignment_target_line_toggle_updates_target_value_label(qtbot, mocked_client_with_dap): |
| 629 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 630 | + wf.plot(arg1="bpm4i", dap="GaussianModel") |
| 631 | + wf.x_mode = "samx" |
| 632 | + |
| 633 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 634 | + wf._alignment_panel.target_toggle.setChecked(True) |
| 635 | + |
| 636 | + assert wf._alignment_target_line is not None |
| 637 | + assert wf._alignment_panel.move_to_target_button.isEnabled() is True |
| 638 | + |
| 639 | + wf._alignment_target_line.setValue(1.5) |
| 640 | + wf._on_alignment_target_line_changed() |
| 641 | + |
| 642 | + assert "1.500" in wf._alignment_panel.target_toggle.text() |
| 643 | + |
| 644 | + |
| 645 | +def test_alignment_move_to_target_uses_draggable_line_value(qtbot, mocked_client_with_dap): |
| 646 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 647 | + wf.plot(arg1="bpm4i", dap="GaussianModel") |
| 648 | + wf.x_mode = "samx" |
| 649 | + |
| 650 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 651 | + wf._alignment_panel.target_toggle.setChecked(True) |
| 652 | + wf._alignment_target_line.setValue(1.25) |
| 653 | + |
| 654 | + move_spy = MagicMock() |
| 655 | + wf.dev["samx"].move = move_spy |
| 656 | + |
| 657 | + wf._alignment_panel.move_to_target_button.click() |
| 658 | + |
| 659 | + move_spy.assert_called_once_with(1.25, relative=False) |
| 660 | + |
| 661 | + |
| 662 | +def test_alignment_mode_toggle_off_keeps_user_dap_curve(qtbot, mocked_client_with_dap): |
| 663 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 664 | + source_curve = wf.plot(arg1="bpm4i") |
| 665 | + dap_curve = wf.add_dap_curve(device_label=source_curve.name(), dap_name="GaussianModel") |
| 666 | + wf.x_mode = "samx" |
| 667 | + |
| 668 | + action = wf.toolbar.components.get_action("alignment_mode").action |
| 669 | + action.trigger() |
| 670 | + action.trigger() |
| 671 | + |
| 672 | + assert wf.get_curve(dap_curve.name()) is not None |
| 673 | + |
| 674 | + |
| 675 | +def test_alignment_panel_removes_deleted_dap_curve_from_fit_list(qtbot, mocked_client_with_dap): |
| 676 | + wf = create_widget(qtbot, Waveform, client=mocked_client_with_dap) |
| 677 | + source_curve = wf.plot(arg1="bpm4i") |
| 678 | + dap_curve = wf.add_dap_curve(device_label=source_curve.name(), dap_name="GaussianModel") |
| 679 | + |
| 680 | + wf.toolbar.components.get_action("alignment_mode").action.trigger() |
| 681 | + wf.dap_summary_update.emit( |
| 682 | + make_alignment_fit_summary(center=1.5), {"curve_id": dap_curve.name()} |
| 683 | + ) |
| 684 | + |
| 685 | + assert dap_curve.name() in wf._alignment_panel.fit_dialog.summary_data |
| 686 | + |
| 687 | + wf.remove_curve(dap_curve.name()) |
| 688 | + |
| 689 | + assert dap_curve.name() not in wf._alignment_panel.fit_dialog.summary_data |
| 690 | + |
| 691 | + |
499 | 692 | def test_curve_set_data_emits_dap_update(qtbot, mocked_client): |
500 | 693 | wf = create_widget(qtbot, Waveform, client=mocked_client) |
501 | 694 | c = wf.plot(x=[1, 2, 3], y=[4, 5, 6], label="test_curve") |
|
0 commit comments