GUI components
The GUI is a PySide6 application organised around a
MainWindow that hosts the four
persistent panels (data browser, plot, fit, Fourier) plus a log dock
and a workspace selector for the time / individual-groups / frequency
views. Non-modal dialog windows (Grouping, Detector Layout, Fit Wizard,
Global Fit Wizard, Run Info, GLE setup) are launched from the main
window. The end-user reference for the GUI lives in
GUI usage; this API page lists the classes for
developers who need to embed or extend the components, not for
day-to-day use.
Main application
Application entry point for the Asymmetry GUI.
Launch with:
python -m asymmetry.gui.app
# or via the installed console script:
asymmetry-gui
Main window
- class asymmetry.gui.mainwindow.MainWindow[source]
Bases:
QMainWindowTop-level application window for the Asymmetry μSR analysis GUI.
Orchestrates all panels (data browser, plot, fit, Fourier, log) and manages the central data flow between them. Also owns project-file save/load and the recent-projects list stored in
QSettings.Project files (
.asymp)The
collect_project_state/restore_project_statepair serialises and deserialises the full application state as a versioned JSON file. Source data files are referenced rather than embedded, so each relevant data file must remain accessible at its original path (or at the same relative path from the.asympfile) for the project to reopen cleanly. Missing files are skipped with aWARNINGlog entry; the rest of the session restores normally.Saved state includes:
List of loaded datasets with source-file paths and field overrides
Co-added (combined) dataset group definitions
Data browser sort column, active filters, and selected runs
Plot panel axis limits and fit-curve overlay
Single-fit and global-fit model selection and parameter table contents
Fourier panel window, zero-pad factor, and display mode
Previously not saved (now persisted where available):
Raw asymmetry / time / error arrays (reloaded from source files)
Fit result statistics (χ², uncertainties) — only the fitted parameter values that were written back to the parameter table
Fourier transform output (cached spectra are now saved when computed)
- eventFilter(obj: QObject, event: QEvent) bool[source]
Record inspector-dock closes into the per-representation memory.
The dock X button sends a Close event; programmatic hide() does not — so this captures exactly the user closes consumed by _apply_inspector_for_domain.
- collect_project_state() dict[source]
Return a full serialisable snapshot of the current application state.
- Returns:
Project state suitable for passing to
save_project().- Return type:
- restore_project_state(state: dict, project_path: str) None[source]
Restore the full application state from a project file state dict.
Owns the
_restoring_projectflag for its duration (the open-project caller also sets it — idempotent) so direct callers get the same suppressions: no dirty-marking, and no FFT compute-on-view seeding. Restore replays domain/selection changes against a HALF-restored window — the workspace state lands before the project model — so a mid-restore seed would record a recipe into the outgoing model, and its in-flight compute would then block the real recipe recompute against the restored one.- Parameters:
state (dict) – Validated project state as returned by
load_project().project_path (str) – Absolute path to the project file (used to resolve relative paths).
- staticMetaObject = PySide6.QtCore.QMetaObject("MainWindow" inherits "QMainWindow": Methods: #39 type=Signal, signature=_background_log(QString), parameters=QString )
Panels
Data Browser
- class asymmetry.gui.panels.data_browser.DataBrowserPanel(parent: QWidget | None = None)[source]
Bases:
QWidgetLogbook-style run table with grouping, sorting, filtering and co-add.
- dataset_selected
- selection_changed
- group_selected
- get_info_requested
- grouping_requested
- assign_profile_requested
(run_numbers, profile_name) — assign the runs to the named grouping profile (schema v17); MainWindow re-resolves and records the change.
- extra_columns_changed
- refit_coadded_requested
- combine_status
- datasets_changed
- fit_group_requested
- show_group_series_requested
- ungroup_requested
- group_membership_changed
- batch_updates()[source]
Defer table rebuilds while adding or removing many datasets.
Inside the block,
_rebuild_table,_sort_tableand_resize_columns_to_contentrecord that they were requested instead of running; the outermost exit replays each at most once. Callers must not read table rows inside the block (model state —_datasets,_display_order— is always current). Nesting is safe.
- set_project_model(project_model: ProjectModel) None[source]
Bind the panel to the canonical group registry (D6).
MainWindow calls this whenever it (re)creates the project model — on construction and after a project load replaces the model — so the panel always reads and mutates the live
data_groups. Collapse flags for ids no longer present are dropped; the group view is rebuilt from the (possibly freshly loaded) registry.
- sync_groups_from_project_model() None[source]
Rebuild the group view from the canonical registry (D6).
Ensures every group carried by a legacy
browser_state.data_groupsblock (a pre-registry project, or a standalone panel restore) exists in the registry, then integrates the registry’s groups into the display order, re-derives multi-membership, and repaints. Idempotent — safe to call after a load rebinds a fresh model or after a browser-only restore.
- all_datasets() list[MuonDataset][source]
Return the browser’s datasets in insertion order.
- next_derived_run_number() int[source]
Reserve a run number for a synthetic or degraded run.
Numbers start at 90001 and skip anything already loaded or reserved this session. Real ISIS run numbers can exceed this (modern runs are six digits), so a file loaded later could carry the same number —
add_datasetkeys entries by run number and would replace the derived run. Acceptable for session-scoped derived data; revisit if derived runs gain a persistent identity.
- release_derived_run_number(number: int) None[source]
Return a reserved derived run number that was never used.
Lets a dialog hand back the number it reserved when generation fails, so a failed Generate does not leave a permanent gap in the SIM series. A number already claimed by a loaded dataset is left untouched.
- add_dataset(dataset: MuonDataset) None[source]
- create_data_group(run_numbers: list[int], name: str | None = None, group_id: str | None = None, collapsed: bool = False, kind: str = 'user', order_key: str = 'run') str | None[source]
Create a group over run_numbers in the canonical registry (D6).
Multi-membership (D2): this adds a group; it never strips its runs from any group they already belong to. Requires at least two valid (loaded) runs. kind is
"user"or"auto"(a machine-minted batch group, D3); order_key seeds the group’s member ordering. Returns the new group id, orNonewhen fewer than two runs are valid or group_id already exists.
- ungroup(group_id: str, *, orphan_series: bool = True) list[str][source]
Dissolve group_id entirely, returning its now-orphaned runs to top level.
Members still belonging to another group keep their rows; only members left with no group return to the ungrouped list (at the group’s former slot). orphan_series (D7) selects the disposition of any series the group owns:
Truefreezes each to a standalone legacy analysis (group_id=None),Falsedeletes them. The host prompts for this choice (seeungroup_requested); the browser passes it straight through and returns the removedbatch_ids (empty when freezing) so the host can clean up their fit slots / panels.
- add_runs_to_group(run_numbers: list[int], group_id: str) bool[source]
Add existing dataset runs into an existing group as a new membership (D2).
Multi-membership: a run already in another group keeps that membership and gains this one (rendered as a marked copy); it is never stolen. A run gaining its first membership leaves the ungrouped list.
- Returns:
Trueif at least one run was added.- Return type:
- remove_runs_from_group(run_numbers: list[int]) bool[source]
Remove each run’s primary group membership (D2).
A run in several groups loses only its primary membership; the earliest remaining copy is promoted to primary. A run’s last membership removal returns it to the ungrouped list. Returns
Truewhen at least one membership was removed.
- multi_period_run_numbers() set[int][source]
Return run numbers of loaded datasets that carry more than one period.
- resizeEvent(event) None[source]
Keep the columns filling the panel as the dock is resized.
Deferred one event-loop turn: the table’s viewport geometry settles via the layout pass after this event, so an immediate fit would read the previous width.
- changeEvent(event) None[source]
Re-apply two-line row heights when fonts change.
The heights are explicit pixels computed from the table font at rebuild time; a font change (UI scale) would otherwise leave the comment line clipped until the next rebuild.
- extra_columns() list[ExtraColumn][source]
Return a copy of the current extra-column definitions (custom + metadata).
- custom_label_fields() list[tuple[str, str]][source]
Return
(label, "custom:<id>")pairs for the user’s custom columns.These are the columns offered as the plot legend label and the parameter trend x-axis. Their per-run values live in
dataset.metadata["custom_fields"]keyed by the same id, so consumers resolve them without reaching back into the browser.
- custom_values_by_run() dict[int, dict[str, str]][source]
Map each known run number to its live custom-column values.
The source of truth for custom columns is
dataset.metadata["custom_fields"](keyed by column id). Exposing it per run lets trend consumers re-link existing batch results to a column that was added or populated after the fit completed (the ordering trap) without re-running the batch. A run with no custom values maps to an empty dict so consumers can clear stale snapshots, not just add.Combined (co-added/subtracted) datasets are included here too: their dataset object lives in
_datasetsunder the combined run number, same as any regular run, so a single pass over_datasetsalready covers them._combined_datasetsmaps a combined run number to its list of source run numbers, not to a dataset, and must not be iterated as(run_number, dataset)pairs.
- angle_x_field() tuple[str, str] | None[source]
Return
(label, id)for the special Angle field, or None if absent.The Angle field is promoted to a first-class trend x-axis rather than offered among the generic custom columns, so consumers route it separately.
- add_extra_column(field_key: str) None[source]
Add a metadata-backed dynamic column to the browser table.
- add_custom_column(label: str) ExtraColumn | None[source]
Create an empty, user-editable custom column and return its definition.
- add_angle_column() ExtraColumn | None[source]
Add the singleton special “Angle (°)” field (numeric degrees per run).
Returns the existing Angle field if one is already present, so the action is idempotent and never produces a duplicate.
- rename_extra_column(column_id: str, new_label: str) bool[source]
Rename a column’s gui-facing label (its source_key is untouched).
- remove_extra_column(field_key_or_id: str) None[source]
Remove an extra column by id or (for metadata columns) source key.
- get_extra_columns() list[str][source]
Return the metadata source keys currently shown (for inclusion tracking).
Custom columns have no metadata source and are intentionally excluded — this drives the Get Info “Include in Data Browser” checkboxes, which only concern NeXus/metadata fields. The from-log pseudo-keys are appended to mirror the fixed T/B columns’ include state.
- set_use_temperature_from_log(enabled: bool) None[source]
Set the global temperature-from-log display option.
- set_dataset_temperature_from_log(run_number: int, enabled: bool) None[source]
Override temperature-from-log display for a single dataset.
- dataset_uses_temperature_from_log(run_number: int) bool[source]
Return whether one dataset is configured to show log temperature.
- set_use_field_from_log(enabled: bool) None[source]
Set the global field-from-log display option (B from the data log).
The analogue of
set_use_temperature_from_log()(WiMDA’sBfromaveinblog): show the mean of the magnetic-field log channel in the B column instead of the header scalar.
- set_dataset_field_from_log(run_number: int, enabled: bool) None[source]
Override field-from-log display for a single dataset.
- dataset_uses_field_from_log(run_number: int) bool[source]
Return whether one dataset is configured to show log field.
- displayed_coordinate(run_number: int) dict[str, float | None][source]
The
field/temperaturethe browser currently displays for a run.Honours the log toggles and per-dataset overrides — the same resolution as the fixed B/T columns (
_field_for_display(),_temperature_for_display()) — so a batch trend and its CSV export plot each run at the abscissa the user sees in the table, not the parked header setpoint. When the browser shows a logged value it replaces the scalar; an axis with no recorded value isNone(off that axis), never0— preserving the trend’s “missing coordinate” semantics rather than the columns’0.0fallback.
- custom_column_value(dataset: MuonDataset, column_id: str) str[source]
Return the stored per-run text for a custom column (
""if unset).
- get_current_dataset() MuonDataset | None[source]
Return dataset on the current table row when that row is a run.
- is_single_group_selected() bool[source]
Return True when the selection contains exactly one group header row.
- get_group_id_for_run(run_number: int) str | None[source]
Return run_number’s primary data-group id, if any (D2).
Compatibility accessor: under multi-group membership a run may belong to several groups; this returns only the primary (first) membership. Callers that need every membership use
get_group_ids_for_run(). (Phase 3 reworks the fit-flow callers that still assume single membership.)
- get_group_ids_for_run(run_number: int) list[str][source]
Return every group id run_number belongs to, primary first (D2).
- get_group_member_run_numbers(group_id: str) list[int][source]
Return run numbers currently belonging to group_id.
- get_all_group_ids() list[str][source]
Return every current group id, from the canonical registry (D6).
- get_dataset(run_number: int) MuonDataset | None[source]
- set_grouping_profile_provider(provider: Callable[[], list] | None) None[source]
Inject a callable returning the project’s live grouping profiles.
The panel derives the per-run profile marker and the Assign-Grouping-Profile context menu from it; a provider (rather than a copied list) keeps the panel current when MainWindow rebuilds the profile list on Apply.
- set_highlighted_runs(run_numbers: set[int] | None) None[source]
Tint run_numbers with the FitSeries membership indicator (red tint).
Pass an empty set or
Noneto clear all highlights. The tint is purely decorative — it never alters the table’s selection state.
- select_runs(run_numbers: set[int] | list[int]) None[source]
Perform a true selection of run_numbers in the browser table.
This is a real selection (drives
selection_changed, updates the fit panel, etc.), distinct from the decorative red tint applied byset_highlighted_runs(). Existing selection is replaced. Runs not currently visible in the table are silently skipped.
- get_selected_datasets() list[MuonDataset][source]
- get_all_datasets() list[MuonDataset][source]
Return all datasets currently present in the browser.
- is_combined_dataset(run_number: int) bool[source]
Return
Truewhen run_number refers to a combined row.
- get_combined_source_datasets(run_number: int) list[MuonDataset][source]
Return hidden source datasets for a combined row.
- rebuild_combined_dataset(run_number: int) MuonDataset | None[source]
Recompute one combined dataset from its hidden source datasets.
- render_logbook_tsv() tuple[str, int][source]
Render the TSV logbook to a string, reading widget/grouping state.
Returns
(content, exported_rows). The render reads the table model (columns, grouping, every dataset) so it must run on the GUI thread; the caller writescontentto disk (off-thread where it matters). The export includes rows hidden by filters or collapsed groups.
- export_logbook_tsv(path: str) int[source]
Render and write the TSV logbook to path (synchronous helper).
- render_logbook_rtf() tuple[str, int][source]
Render the RTF logbook to a string, reading widget/grouping state.
Returns
(content, exported_rows). Likerender_logbook_tsv(), the render reads the table model and must run on the GUI thread; the caller writes the bytes. Includes rows hidden by filters/collapsed groups.
- export_logbook_rtf(path: str) int[source]
Render and write the RTF logbook to path (synchronous helper).
- apply_degrade_statistics(run_number: int, factor: float, seed: int) MuonDataset | None[source]
Resample run_number’s histograms by factor; add the derived run.
Returns the new browser dataset, or
Nonewhen the source has no detector histograms or the factor is invalid. The source run is never modified (WiMDA degraded in place; see docs/porting/simulate-mode/).
- sort_by_run_number_if_unordered() bool[source]
Apply an ascending run-number sort when rows aren’t already in run order.
Called after a multi-file load (F8): an OS file picker can hand files back in a scrambled order, so the browser’s insertion order need not be ascending by run — which makes a shift-range selection silently non-contiguous. This only nudges the default case: if the user has already chosen a sort column, or the rows are already ascending by run, it does nothing (and never reorders a run inside a data group). Returns
Truewhen it applied the sort.
- shutdown_workers(timeout_ms: int = 10000) None[source]
Stop background combine work (call from the main window’s closeEvent).
The browser is a docked widget, so its own
closeEventnever fires; the main window shuts it down alongside the other panel runners.
- add_combined_dataset(source_run_numbers: list[int], *, sign: int = 1, operation: str | None = None) int | None[source]
Recreate a combined row programmatically (
.asympload).sign=+1co-adds;sign=-1subtracts.operationdisambiguates the subtractions:"subtract_signed"is the symmetric N-run signed co-subtract (sample − every other source), any other value (orNonewithsign=-1) is the two-run reference subtraction. Co-add requires identical grouping; the subtractions only need the count-level invariants (combine_runschecks them), so the stricter grouping gate is skipped for them.
- staticMetaObject = PySide6.QtCore.QMetaObject("DataBrowserPanel" inherits "QWidget": Methods: #33 type=Signal, signature=dataset_selected(int), parameters=int #34 type=Signal, signature=selection_changed() #35 type=Signal, signature=group_selected(QString), parameters=QString #36 type=Signal, signature=get_info_requested(int), parameters=int #37 type=Signal, signature=grouping_requested(int), parameters=int #38 type=Signal, signature=assign_profile_requested(PyObject,QString), parameters=PyObject, QString #39 type=Signal, signature=extra_columns_changed() #40 type=Signal, signature=refit_coadded_requested(PyObject), parameters=PyObject #41 type=Signal, signature=combine_status(QString), parameters=QString #42 type=Signal, signature=datasets_changed() #43 type=Signal, signature=fit_group_requested(QString), parameters=QString #44 type=Signal, signature=show_group_series_requested(QString), parameters=QString #45 type=Signal, signature=ungroup_requested(QString), parameters=QString #46 type=Signal, signature=group_membership_changed() )
Plot panel
- class asymmetry.gui.panels.plot_panel.PlotPanel(parent: QWidget | None = None, *, domain: str = 'time')[source]
Bases:
QWidgetMatplotlib canvas for time- and frequency-domain plots.
Notes
The bunch factor controls both the plotted representation and the dataset prepared for fitting in the GUI. The stored source dataset remains unchanged, and any rebinned fit dataset is produced as a temporary copy.
- bunch_factor_changed
- fit_range_changed
- view_limits_changed
- polarization_axis_changed
- fit_target_projection_changed
Emitted with the projection label when a stacked subplot is clicked to become the single-fit target (multi-subplot / ALL view only).
- overlay_toggled
- waterfall_changed
Emitted when the waterfall toggle or manual-Δ field changes and the host should re-render the overlay (mirrors
overlay_toggled).
- time_view_changed
- cursor_coords_changed
- moments_window_changed
window in canonical MHz.
- Type:
Spectral-moments overlay drags (frequency panel)
- moments_cutoff_changed
- moments_drag_finished
A moments-handle drag has ended (mouse released after an actual move): the signal for “recompute with full bootstrap uncertainty now”.
- grouping_hint_requested
The user clicked the “Open Grouping…” link in the transverse-field grouping-hint bar (see
set_grouping_hint()); the host opens the grouping dialog for the current run.
- grouping_hint_dismissed
The user dismissed the grouping-hint bar (the ✕ button); the host records the dismissal so the nudge stays hidden for that run.
- set_grouping_hint(text: str | None) None[source]
Show or hide the transverse-field grouping-nudge bar.
textmay contain rich text with an<a href=…>Open Grouping…</a>link; a falsy value hides the bar. Idempotent and safe to call on a matplotlib-less panel (it simply no-ops).
- set_frequency_axis_mode(mode: str) None[source]
Set the frequency x-axis transform mode programmatically.
mode is one of
"absolute","shift", or"relative_ppm".
- is_frequency_axis_relative_to_reference() bool[source]
Return whether the frequency x axis is a reference-shifted axis.
True for both shift modes (
"shift"and"relative_ppm"); the phase-estimation window logic narrows around the reference whenever the view is reference-shifted, so this stays a single predicate.
- get_frequency_view_window_mhz(*, reference_dataset: MuonDataset | None = None) tuple[float, float] | None[source]
Return the current frequency-view x window in canonical absolute MHz.
The window is inverted through the ACTIVE dataset’s resolved reference — the phase-estimation window this feeds is a per-active-run concept, so an explicit reference_dataset (when the caller knows the active run) overrides the stored active reference.
- decimation_chip_text(ax=None) str | None[source]
Return the decimation chip text on ax (None when not displayed).
- set_custom_label_fields(fields: list[tuple[str, str]]) None[source]
Set the data-browser custom columns offered as legend-label options.
fieldsis a list of(display_label, "custom:<id>")pairs. The combo is rebuilt (selection preserved) and, if the active label field is a custom column whose label changed, the plot is redrawn so the legend tracks the rename.
- set_custom_values_by_run(values_by_run: dict[int, dict[str, str]]) None[source]
Set the
run_number -> {column-id: value}custom-label fallback.Datasets that do not carry
custom_fieldsinline — averaged FFT spectra copy their metadata from a per-group source that has none — can still resolve a custom-column label from this host-provided map, keyed by run number. Redraws when a custom field is active so the change is reflected immediately (e.g. a value edited after the spectrum was drawn).
- set_time_view_modes(modes: list[str], current_mode: str | None = None) None[source]
Update the explicit time-domain view selector.
- set_current_time_view_mode(mode: str, *, emit_signal: bool = False) None[source]
Select the active time-domain view mode.
- set_overlay_enabled(enabled: bool, *, emit_signal: bool = False) None[source]
Set overlay mode from state restore or external UI events.
- set_waterfall_enabled(enabled: bool, *, emit_signal: bool = False) None[source]
Set waterfall mode from state restore or external UI events.
- waterfall_offset() float | None[source]
Return the manual waterfall Δ, or
Nonewhen spacing is automatic.
- set_waterfall_offset(offset: float | None, *, emit_signal: bool = False) None[source]
Set the manual waterfall Δ (
Nonefor auto), updating the field.
- set_active_label_group(group_id: str | None) None[source]
Switch legend label-field context between ungrouped and Data Group views.
- set_counts_rebunch_provider(provider: Callable[[MuonDataset, int], tuple[ndarray, ndarray, ndarray] | None] | None) None[source]
Install the counts-first re-reduction hook for display bunching.
provider(dataset, factor)re-runs the dataset’s recorded reduction (deadtime/background corrections included) with the panel’s bunch factor multiplied onto the groupingbunching_factor, returning(time, asymmetry, error)on the percent scale — orNonewhen counts-first bunching does not apply (no raw histograms, non-fixed binning, period-combined curves), in which caseget_analysis_dataset()falls back to the curve-level rebin.
- get_analysis_dataset(dataset: MuonDataset | None) MuonDataset | None[source]
Return the dataset that should be used for plotting and fitting.
When the bunch factor is 1, the original dataset is returned. For a larger bunch factor, a rebinned MuonDataset copy is returned with the same metadata and run association. Runs that still carry raw histograms are re-reduced counts-first through the host-injected provider; histogram-less curves (co-added data, G±R combinations) keep the curve-level
rebin().
- update_frequency_reference(dataset: MuonDataset | None) None[source]
Public entry-point: sync the reference field from dataset metadata.
Call this whenever the active dataset changes so the Reference spinbox shows the correct value even before an FFT has been computed.
- get_fit_dataset(dataset: MuonDataset | None) MuonDataset | None[source]
Return dataset restricted to the currently selected fit range.
- get_fit_range() tuple[float | None, float | None][source]
Return the active fit range as (x_min, x_max).
- get_view_limits() tuple[float, float, float, float][source]
Return the currently displayed x/y limits from the toolbar fields.
- view_reframed_on_last_draw() bool[source]
Whether the most recent plot draw first-paint framed the axes.
Lets a caller that re-applies preserved limits after a draw (the frequency render path) tell a same-content recompute — which must keep the user’s window — apart from a genuine first paint, whose freshly computed smart framing must survive rather than be overwritten by stale preserved defaults.
- set_view_limits(x_min: float, x_max: float, y_min: float, y_max: float) None[source]
Apply x/y limits through the existing toolbar-backed controls.
- set_decimation_enabled(enabled: bool, *, redraw: bool = True) None[source]
Enable or disable display-only plot decimation.
- set_bunch_factor(value: int, *, emit_signal: bool = True) None[source]
Set the plot-panel bunch factor and optionally emit the normal signal.
- set_custom_x_axis_label(label: str | None) None[source]
Pin (or clear) a custom x-axis label that survives replots.
A replot recomputes and overwrites the x-axis label, so a caller that wants a bespoke label — for example a Fourier annotation workflow labelling a hyperfine-coupling axis — pins it here. It is re-applied after every render (including the show-time replot) instead of being wiped. Pass
Noneto restore the computed label.
- add_persistent_frequency_marker(freq_mhz: float, label: str = '') None[source]
Pin a vertical frequency-line marker that survives replots.
freq_mhzis a canonical (absolute) frequency in MHz; it is display-unit converted at draw time so the marker tracks the active axis unit. The marker is recreated on every render, so — unlike a one-offaxvlinedrawn straight onto the axis — it is not wiped by the show-time replot. Use for Fourier line labels (ν₁, ν₂, A_µ, γ_μ·B).
- clear_persistent_frequency_markers() None[source]
Remove every pinned frequency-line marker and redraw.
- get_current_polarization_axis() str | None[source]
Return current polarization selector key (
P_*orALL).
- set_projections(projections: list[dict], selected: list[str] | None = None) None[source]
Show/update the projection chip bar, or hide it when unavailable.
projectionsis an ordered list of{"label", "tint"?}dicts;selectedis the subset of labels to show as subplots (defaults to all). The bar (and any multi-projection behaviour) is suppressed when fewer than two projections exist.
- selected_projection_labels() list[str][source]
Return the projection labels currently selected.
The chip bar is the source of truth once populated; the stored copy is the fallback during project restore, before the bar has been rebuilt.
- projection_tint(label: str | None) str | None[source]
Return the frame/identity tint for a projection label, if any.
- fit_target_projection() str | None[source]
Return the projection whose subplot is the active single-fit target.
Only meaningful in the stacked multi-subplot view;
Noneotherwise.
- set_fit_target_projection(label: str | None, *, emit: bool = True) None[source]
Mark label’s subplot as the single-fit target and redraw its box.
The fit target is also the focus for the manual Y-limit controls, so a change caches the outgoing subplot’s limits and surfaces the incoming subplot’s into the Y fields.
- plot_vector_subplots(datasets_by_axis: dict[str, list[MuonDataset]]) None[source]
Render the selected projections as stacked subplots.
- plot_grouped_time_domain_subplots(datasets: list[MuonDataset]) None[source]
Render grouped time-domain traces as stacked subplots.
- plot_maxent_reconstruction(datasets: list[MuonDataset], *, combined: bool = False) None[source]
Overlay the MaxEnt time-domain reconstruction on the measured data.
Each dataset carries the observed signal in
asymmetryand the forward-model reconstruction + weighted residual inmetadata['maxent_model']/['maxent_residual']. Two layouts share the same data and χ²:per-group (default): one main axis (data points + model line) above a residuals strip per group, stacked vertically;
combined (
combined=True): every group’s data+model overlaid on a single colour-coded axis above one shared residuals strip.
The displayed χ² is the engine’s by construction (the residuals are
(data − model)/σ), so both layouts report the same total.
- set_fit_range(x_min: float, x_max: float) None[source]
Set fit range limits and refresh visual handles.
- plot_datasets(datasets: list[MuonDataset]) None[source]
Plot multiple datasets on the same axes with per-dataset colours.
Each dataset is assigned a colour from matplotlib’s default cycle (C0, C1, …). Any stored fit curve for a run is drawn in a matching style, with high-contrast overrides for period-mode datasets. Low-count (grey) points are still drawn at reduced opacity. The axes limits are initialised from the combined data extent on the first call, then held fixed on subsequent redraws.
Delegates to
plot_dataset()when datasets has exactly one entry so that the single-dataset code path (limit initialisation, fit-range handling, etc.) is exercised unchanged.
- set_diamagnetic_overlay(time_us: ndarray | None, signal: ndarray | None, *, run_number: int | None = None) None[source]
Set (or clear) the diamagnetic-fit curve drawn on the time-domain view.
Passing
Nonefor the arrays clears it. The overlay is tagged with run_number and only drawn while that run is displayed, so it never lingers on an unrelated run; it is redrawn on the nextplot_dataset()call for a time-domain panel.
- plot_dataset(dataset: MuonDataset) None[source]
Plot a dataset, optionally rebinned according to the bunch factor.
The input dataset is stored unchanged as the current dataset. If the bunch factor is greater than 1, temporary rebinned arrays are created for plotting. The source dataset itself is never mutated.
- current_frequency_dataset() MuonDataset | None[source]
Return the active frequency-domain dataset, or
None.
- active_spectrum_for_moments() tuple[ndarray, ndarray, ndarray | None, str] | None[source]
W15 accessor:
(x, amplitude, errors, x_unit)for the active spectrum.xis the canonical absolute MHz axis (the unit-invariant the moments feature works in) andx_unitis"mhz"; the caller converts to its chosen field unit. ReturnsNonewhen no spectrum is shown or the active spectrum is a correlation axis (a hyperfine coupling, not a field).
- set_moments_overlay(*, window_mhz: tuple[float, float] | None, cutoff_fraction: float, peak_amplitude: float | None, visible: bool) None[source]
Show/update the moment window + cutoff line on the spectrum plot.
window_mhz is the analysis window in canonical absolute MHz; the cutoff line is drawn at
peak_amplitude · cutoff_fraction. Drawn only on the frequency panel.
- plot_fit(t_fit, y_fit, label: str = 'Fit', component_curves: list[tuple[str, object]] | None = None, fit_result: object | None = None, fit_function: str | None = None, run_number: int | None = None) None[source]
Overlay a fit curve on the current plot.
The fit curve will be retained even when bunching or limits change.
- Parameters:
t_fit (array) – Time points for the fit curve.
y_fit (array) – Fitted asymmetry values.
label (str, optional) – Label for the fit curve in the legend.
fit_result (object, optional) – FitResult containing chi_squared, parameters, uncertainties, etc.
run_number (int, optional) – Run the fit was computed for. In a multi-run overlay stacked view
_current_datasetpoints at the first projection’s last run, not the fitted (selected) run, so the caller passes the fitted run explicitly to keep the overlay key and the persisted single-fit slot on the same run. Defaults to inferring it from_current_dataset.
- set_global_fits(fit_curves_dict: dict) None[source]
Set fit curves from global fitting.
- Parameters:
fit_curves_dict (dict) – Dictionary mapping run_number -> (t_fit, y_fit, label, component_curves), (t_fit, y_fit, label, component_curves, fit_result), or (t_fit, y_fit, label, component_curves, fit_result, fit_function), or (t_fit, y_fit, label, component_curves, fit_result, fit_function, axis_key).
- clear(*, message: str | None = None, preserve_view_state: bool = False) None[source]
Clear the plot and reset stored data.
When message is given, draw it as a centred grey placeholder over the (axis-off) plot area — the empty-state pattern used by
asymmetry.gui.panels.alc_panel.AlcPanel.clear(). The frequency panel passes a message to prompt the user to compute a spectrum (an FFT is computed on demand, never automatically); every other caller leaves messageNoneand gets an unchanged blank plot.preserve_view_state keeps the frame latches (
_limits_initialized,_limits_user_locked,_framed_identity) rather than resetting them. It is for transient empty states — browsing past an uncomputed run, or an empty-spectrum render — where the blank is momentary and the user’s chosen window must survive to the next compute. Genuine teardown (project close/new, dataset removal) leaves itFalseso a fresh view reframes from scratch. It does not touch the spin fields, which clear() never reset; callers re-apply the preserved limits explicitly.
- resizeEvent(event) None[source]
Keep the canvas width aligned with the viewport during grouped scrolling.
- clear_fits_for_runs(run_numbers: list[int]) int[source]
Clear stored fit overlays for the provided run numbers.
- get_current_plot_export_data(datasets: list[MuonDataset] | None = None) list[dict] | None[source]
Return export payloads for all displayed datasets.
Returns a list of dicts (one per displayed dataset), or None when nothing is available to export.
- export_plotted_data_as_text() None[source]
Export the plotted curve(s) as plain text, without the GLE machinery.
Reuses the same
.dat/.fitwriters (and their provenance header) as the GLE export, but writes only the text files — no.gleplotfolder, GLE script, or compile. The content switch (data only / data + fit / fit only) mirrors WiMDA’s stData/stBoth/stFit.
- export_plots_to_gle() None[source]
Export current main-plot view as GLE using gleplot.
Time-domain data is plotted with error bars (no connecting lines) and fit curves with lines (no markers). Frequency-domain (FFT/MaxEnt) views mirror the on-screen spectrum instead: a piecewise-linear line in the current display unit plus a light shaded ±1σ band. File names are derived from the Label dropdown value for each dataset.
- export_current_plot() None[source]
Export current main-plot view as GLE (with optional compiled output).
- shutdown_workers(timeout_ms: int = 10000) None[source]
Stop this panel’s background tasks (GLE export compiles).
- set_rrf_feature_enabled(enabled: bool) None[source]
Enable/disable the whole RRF surface (the Advanced toggle, app-level).
Forwards to the RRF controls (which appear/disappear under the usual view condition) and redraws so the display reverts to or from the rotating frame immediately.
- rrf_has_active_parameters() bool[source]
True when the RRF controls carry an active frame (enabled + ν₀ > 0).
Independent of the feature toggle: used on project open to decide whether a stored RRF configuration should auto-enable the toggle so the user’s analysis is not silently hidden.
- rrf_fit_frequency_mhz() float | None[source]
Frame frequency ν₀ (MHz) when an RRF fit should be performed, else None.
The fit auto-couples to the plot’s RRF controls: a single composite fit runs in the rotating frame exactly when the RRF display is active (the feature is on, the controls are enabled with ν₀ > 0, and the FB-asymmetry time view is showing). The fit consumes raw data with this offset and reports lab-frame frequencies (δν + ν₀).
- get_state() dict[source]
Return a serialisable snapshot of the plot panel state.
This captures the bunch factor, axis limits, the currently displayed run number, and any stored fit curves. Fit curve arrays are serialised as plain Python lists for JSON compatibility.
- Returns:
Plot state suitable for inclusion in a project file.
- Return type:
- restore_state(state: dict, dataset: MuonDataset | None = None) None[source]
Restore plot panel state from a saved dict.
- Parameters:
state (dict) – Plot state as returned by
get_state().dataset (MuonDataset, optional) – Dataset to re-plot after restoring limits. If None no plot is drawn, but all other state (limits, bunch factor, fit curves) is still applied.
- staticMetaObject = PySide6.QtCore.QMetaObject("PlotPanel" inherits "QWidget": Methods: #33 type=Signal, signature=bunch_factor_changed(int), parameters=int #34 type=Signal, signature=fit_range_changed(double,double), parameters=double, double #35 type=Signal, signature=view_limits_changed(double,double,double,double), parameters=double, double, double, double #36 type=Signal, signature=polarization_axis_changed(QString), parameters=QString #37 type=Signal, signature=fit_target_projection_changed(QString), parameters=QString #38 type=Signal, signature=overlay_toggled(bool), parameters=bool #39 type=Signal, signature=waterfall_changed() #40 type=Signal, signature=time_view_changed(QString), parameters=QString #41 type=Signal, signature=cursor_coords_changed(PyObject), parameters=PyObject #42 type=Signal, signature=moments_window_changed(double,double), parameters=double, double #43 type=Signal, signature=moments_cutoff_changed(double), parameters=double #44 type=Signal, signature=moments_drag_finished() #45 type=Signal, signature=grouping_hint_requested() #46 type=Signal, signature=grouping_hint_dismissed() )
Fit panel
- class asymmetry.gui.panels.fit_panel.FitPanel(parent: QWidget | None = None)[source]
Bases:
QWidgetFit setup and results panel with tabbed interface.
Contains tabs for single dataset fitting and global (multi-dataset) fitting.
- fit_completed
- preview_requested
- global_fit_started
- global_fit_completed
- grouped_fit_completed
- add_single_fit_to_series_requested
- fit_range_edit_committed
- batch_seeding_mode_changed
- tab_changed
- set_active_projection_label(projection: str | None, tint: str | None = None) None[source]
Show/hide the ‘Fitting: <projection>’ echo for the bound projection.
tintcolours the text to match the projection’s subplot frame.
- set_batch_seeding_mode(mode: str) None[source]
Forward the batch-series seeding mode to the Batch tab.
- set_rrf_frequency_provider(provider: Callable[[], float | None]) None[source]
Forward the rotating-frame ν₀ provider to the single-fit tab.
- set_domain(domain: str) None[source]
Switch the fit panel between time- and frequency-domain workflows.
- set_dataset(dataset: MuonDataset | None) None[source]
Set the current dataset for single fitting tab.
D5 “refresh-unless-fitted”: three branches decide what the form shows.
PROTECTED — the restore mediator (
_single_fit_restore_provider) hands back a non-empty payload: a genuine recorded fit for this (run, representation, projection) slot, restored verbatim (provenanceown_slot, no badge). This covers both a single fit and a batch/global write-back — the mediator reconstructs a payload from a batch member’s persistedFitSlotwhen it has noui_stateof its own (seeMainWindow._single_fit_restore_payload), so protection here is driven purely by “did the mediator hand back content”, with no separate probe needed. Never auto-overwritten.An empty mediator payload: the mediator has a real opinion this exact slot was checked and confirmed unfit (a genuine projection, or the default slot once another projection has been fit). REFRESHABLE: once any fit exists in the session, refresh onto it (D5 — even a “domain default” is refreshable); otherwise blank to the domain default exactly as before (this slot was positively checked, so there is nothing to carry from a previous run either).
No mediator opinion at all (
None— a legacy/no-mediator run, or the default slot with no other projection info): REFRESHABLE via_refresh_single_fit_form, which prefers the session’s most recently fitted state, falling back to today’s pre-D5 behaviour (this run’s own cached form, or the previously active run’s displayed form) only once nothing has been fitted yet.
A dismissable badge (D2/F6) surfaces (B)/(C) whenever a source run is named — carry-forward itself is kept (useful for run series), but the panel must say so instead of silently presenting another run’s values as this run’s own.
- set_single_fit_restore_provider(provider: Callable[[MuonDataset | None], dict | None] | None) None[source]
Install the per-projection single-fit restore mediator (or clear it).
The main window passes a callable that maps the dataset being bound to the persisted single-fit form payload for the active
(run, representation, projection)slot — orNoneto defer to the panel’s own run-keyed state (the default / legacy-project path).
- get_single_form_state() dict[source]
Return the single-fit form payload (no per-run/domain wrapping).
This is exactly what
restore_single_fit_ui()consumes, so it is the payload the main window stores as a slot’sui_state.
- restore_single_fit_ui(payload: dict | None) None[source]
Restore (or blank) the single-fit form from a slot
ui_statepayload.A populated dict restores the form verbatim; an empty dict (or
None) blanks it — an unfit projection must never inherit another projection’s fit. The run-keyed blob is deliberately not touched: it stays the per-run store that global seeding reads, while the per-projection slot is the source of truth for the single-fit form.
- set_datasets(datasets: list[MuonDataset]) None[source]
Set the datasets for the global fitting tab, tracking all datasets fed in.
- batch_datasets() list[MuonDataset][source]
Return the datasets configured for the batch/integral-scan.
- set_bound_group(group_id: str | None, name: str | None = None) None[source]
Bind the Batch tab to a data group (“Fit this group…”, D1).
- bound_group_id() str | None[source]
Return the id of the data group the Batch tab is bound to, or
None.
- set_frequency_missing_spectra_status(missing_run_numbers: list[int], cached_count: int) None[source]
Show frequency-domain global fit status for selected uncached runs.
- is_grouped_time_domain_mode() bool[source]
Return whether the global tab is in grouped time-domain mode.
- set_fit_blocked(blocked: bool, reason: str = '') None[source]
Apply fit-action blocking to both single and global tabs.
- set_fit_range_display(x_min: float | None, x_max: float | None) None[source]
Forward fit-range display update to both single and global tabs.
- single_fit_formula_string() str | None[source]
Return the active single-fit formula string, if available.
- single_fit_model_and_seed() tuple[CompositeModel, ParameterSet][source]
Return the active single-fit model and seed (for headless re-fits).
- global_fit_formula_string() str | None[source]
Return the active global-fit formula string, if available.
- clear_fits_for_runs(run_numbers: list[int]) int[source]
Clear cached single/global fit state for specific dataset runs.
- get_single_state_for_run(run_number: int) dict | None[source]
Return current single-fit state for one run, if available.
- get_single_fit_wizard_cache_for_run(run_number: int) tuple[FitWizardRecommendation | None, dict[str, object] | None, str][source]
- persist_single_fit_wizard_cache_for_run(run_number: int, recommendation: FitWizardRecommendation, *, signature: dict[str, object] | None = None, log_text: str = '') None[source]
- build_single_fit_payload_from_slot(model: dict | None, template_parameters: list[dict] | None, result: dict | None) dict | None[source]
Reconstruct a single-fit-tab restore payload from a persisted
FitSlot.Batch/global-member
FitSlot``s are "pointer slots": they carry a genuine fitted ``model/resultbut never aui_state(only the single-fit GUI path writes one). D5 protects any run with a recorded result, soMainWindow._single_fit_restore_payloadcalls this to synthesise a payload rather than treating such a run as unfit — most relevantly across a project reload, before this session’sregister_global_fit_resultscache exists. ReturnsNonewhen there is nothing usable to build from.
- register_global_fit_results(results_by_run: dict[int, tuple[object, object, object]]) None[source]
Persist per-run single-tab state using the latest successful global fit.
- restore_domain_state(domain: str, state: dict | None) None[source]
Restore serialisable fit state for one fitting domain.
The blob’s
domaintag (written byget_domain_state()) must match the requested domain — a mismatch means a stale/mis-routed payload and is refused rather than silently applied to the wrong form (F21c).
- get_grouped_state() dict[source]
Return the grouped-fit classification (physics roles + nuisance block).
- single_fit_range_text() str | None[source]
Active single-fit range as a provenance string (see SingleFitTab).
- batch_fit_range_text() str | None[source]
Active batch/global/grouped fit range as a provenance string.
- send_single_model_to_batch() bool[source]
Copy the single-fit tab’s model and current seeds into the Batch tab.
Returns
Truewhen a model was sent. The Single ⇄ Batch flow: build a model in Single, send it to seed a batch over the selected runs. The batch parameter seeds are taken from the single tab’s current table values (which reflect the latest fit once one has run), so the batch starts from the values the user just set rather than model defaults or stale preserved state (BUG B8c).
- restore_ui_state(state: dict) None[source]
Restore serialisable UI state for the fit panel container.
- staticMetaObject = PySide6.QtCore.QMetaObject("FitPanel" inherits "QWidget": Methods: #33 type=Signal, signature=fit_completed(PyObject,PyObject,PyObject), parameters=PyObject, PyObject, PyObject #34 type=Signal, signature=preview_requested(PyObject,PyObject,PyObject), parameters=PyObject, PyObject, PyObject #35 type=Signal, signature=global_fit_started() #36 type=Signal, signature=global_fit_completed(PyObject,PyObject), parameters=PyObject, PyObject #37 type=Signal, signature=grouped_fit_completed(PyObject,PyObject), parameters=PyObject, PyObject #38 type=Signal, signature=add_single_fit_to_series_requested() #39 type=Signal, signature=fit_range_edit_committed(double,double), parameters=double, double #40 type=Signal, signature=batch_seeding_mode_changed(QString), parameters=QString #41 type=Signal, signature=tab_changed(int), parameters=int )
Fourier panel
- class asymmetry.gui.panels.fourier_panel.FourierPanel(parent: QWidget | None = None, *, settings: QSettings | None = None)[source]
Bases:
QWidgetControls for frequency-domain analysis.
- settings_changed
Emitted (debounced) whenever a control feeding get_state() changes, except the spectral-moments widget (it does not affect the computed spectrum). Suppressed during programmatic restores — see _suppress_settings_signal_scope.
- set_compute_scope(count: int) None[source]
Reflect the compute target count in the primary button’s label.
"Compute FFT"for a single-run scope;"Compute FFT (N runs)"when the Data-Browser selection puts N > 1 runs in scope — the button always computes the full selection, and the label makes that scope visible before clicking.
- exclusion_ranges() list[tuple[float, float]][source]
Return the editable
(centre, half-width)exclusion ranges.
- set_background_hint(text: str | None) None[source]
Show the inherited grouping-background state above the FFT button (F3).
textis the resolved mode description (e.g."tail-fit") orNonewhen no grouping background correction is active. Rendered as the footer hint.
- set_fft_status(message: str, *, success: bool = False) None[source]
Set the footer status line for a computed-spectrum outcome.
A successful compute is reported with the green success chip; other messages fall through as an informational (accent) chip.
- set_stale(reason: str | None) None[source]
Show or hide the “spectrum out of date” banner.
A non-empty
reasonshows the banner with that reason folded into the fixed message;Noneor an empty string hides it.
- set_overlay_mismatch(mismatched: bool) None[source]
Show or hide the “overlay mixes settings” banner.
Independent of
set_stale(): an overlay can mix spectra computed under different settings even when the active run’s own displayed spectrum is in sync with the live panel state, so the two banners can be shown together.
- is_overlay_mismatched() bool[source]
Return whether the overlay-mismatch banner is currently shown.
- group_phase_table() dict[int, float][source]
Return the per-group phase table as
{group_id: phase_deg}.
- group_enabled_table() dict[int, bool][source]
Return the per-group inclusion table as
{group_id: enabled}.
- selected_group_ids() list[int][source]
Return the ordered list of detector groups enabled for grouped FFTs.
- set_group_definitions(group_names: dict[int, str], phase_table: dict[int, float] | None = None, enabled_table: dict[int, bool] | None = None) None[source]
Populate the editable group-phase table for the active run.
- set_group_phases(phase_table: dict[int, float], *, auto_filled: bool = False) None[source]
Update existing group-phase rows without rebuilding the table.
- set_apodisation_suggestion(window: str, time_constant_us: float) None[source]
Fill a matched-apodisation suggestion into the filter controls.
Deliberately does NOT suppress
settings_changed: filling the values is itself a settings change, and the staleness banner engaging is the designed flow (suggestion -> banner -> explicit Compute FFT applies it).
- set_group_enabled(enabled_table: dict[int, bool]) None[source]
Update existing group-inclusion rows without rebuilding the table.
- group_auto_filled_ids() set[int][source]
Return group IDs whose phase cells were last auto-estimated.
- group_phase_state() dict[str, object][source]
Return the current group-phase UI state for one dataset/run.
- restore_group_phase_state(state: dict[str, object] | None, group_names: dict[int, str]) None[source]
Restore per-run group-phase state into the table.
- set_average_summary(*, mean_error: float, peak_signal_to_noise: float, group_count: int) None[source]
Show a short summary for an averaged grouped FFT spectrum.
- property moments_widget: SpectralMomentsWidget
The spectral-moments control hosted in the advanced stack.
- staticMetaObject = PySide6.QtCore.QMetaObject("FourierPanel" inherits "QWidget": Methods: #33 type=Signal, signature=settings_changed() )
Log panel
- class asymmetry.gui.panels.log_panel.LogPanel(parent: QWidget | None = None)[source]
Bases:
QWidgetScrollable log with per-category tag colouring.
Capped to the most recent
_MAX_LOG_BLOCKSentries viaQTextDocument.setMaximumBlockCountso a long-running session’s log cannot grow the widget (and the app’s memory footprint) without bound.- entry_count_changed
Emitted with the running entry count (feeds the dock header’s badge).
- log(message: str, *, tag: str = '') None[source]
Append a timestamped entry, optionally categorised by tag.
- staticMetaObject = PySide6.QtCore.QMetaObject("LogPanel" inherits "QWidget": Methods: #33 type=Signal, signature=entry_count_changed(int), parameters=int )
Dialogs
Grouping dialog
- class asymmetry.gui.windows.grouping_dialog.GroupingDialog(datasets: list[MuonDataset], *, profiles: list[GroupingProfile] | None = None, overridden_run_numbers: list[int] | None = None, assigned_profiles: dict[int, str] | None = None, selected_run_number: int | None = None, selected_run_numbers: list[int] | None = None, parent=None)[source]
Bases:
QDialogProfile editor for detector grouping.
The dialog edits an in-memory draft grouping profile (a
GroupingProfile). Each run of a fingerprint(instrument, histogram_count)follows its assigned profile — several profiles can be in concurrent use (schema v17, e.g. one per sample), with one flagged the fingerprint’s default for newly loaded runs. The scope panel moves runs between profiles (“Assign to ▸”); per-run divergence is an explicit “release from profile” exception, with the assignment kept as the base profile Reattach returns the run to. Applying the edited profile reaches only the runs following it. Nothing touches the project or runs until Apply.The scope panel is the selector: the run selected there is the one the form previews and edits, and the editing target follows that run’s status. An inheriting run edits the profile draft (through a flat
run.groupingpayload — seeasymmetry.gui.windows.grouping.profile_bridge); an overridden run edits that run’s own override draft, seeded once from its stored payload and kept inself._override_drafts. Selecting never prompts — each target keeps its draft — and Apply commits everything dirty: the profile to every inheriting run plus each edited override to its own run. The only guard is closing the window with uncommitted changes.- Parameters:
datasets – Datasets available in the active project. Datasets without raw histograms are ignored.
profiles – The project’s existing grouping profiles. When omitted (or empty for the current fingerprint), the draft is synthesized from the current/reference run’s payload as
"Default (<instrument>)".assigned_profiles – Optional
{run_number: profile_name}map of each run’s recorded profile assignment (schema v17). Runs without an entry — or whose entry no longer names a profile of their fingerprint — are treated as following the fingerprint’s default profile.selected_run_number – Optional run number used as the initially selected run.
selected_run_numbers – Optional run numbers; only the first choice steers the initial selected run / fingerprint. No longer a broadcast selection.
parent – Parent Qt widget.
- __init__(datasets: list[MuonDataset], *, profiles: list[GroupingProfile] | None = None, overridden_run_numbers: list[int] | None = None, assigned_profiles: dict[int, str] | None = None, selected_run_number: int | None = None, selected_run_numbers: list[int] | None = None, parent=None) None[source]
Create a grouping profile editor for project datasets.
- staticMetaObject = PySide6.QtCore.QMetaObject("GroupingDialog" inherits "QDialog": )
- get_grouping_result() dict[str, Any] | None[source]
Return the applied grouping payload for inheriting runs.
The dialog is a profile editor: on Apply the draft is saved as the active profile and resolved onto every inheriting run of the instrument. The returned dict is the flat grouping payload the main window’s
_apply_grouping_settings_to_datasetconsumes, withrun_numbersset to the inheriting runs. Per-run override edits are carried separately inget_profile_result().- Returns:
Grouping payload plus
run_numbers(inheriting runs).Nonewhen no run histograms are available.- Return type:
dict or None
- get_profile_result() dict[str, Any] | None[source]
Return the profile-editor result for the main window to reconcile.
- Returns:
{"profile": GroupingProfile, "inheriting": set[int], "released": set[int], "newly_released": set[int], "newly_reattached": set[int], "preview_run_number": int, "override_edits": {run_number: payload}}— the saved draft profile, the scope reconciliation, and every dirty per-run override payload to apply to its run alone.Nonewhen no run is available.- Return type:
dict or None
- property draft_profile: GroupingProfile
The current draft profile (a copy synced from the form).
Detector layout dialog
- class asymmetry.gui.windows.detector_layout_dialog.DetectorLayoutDialog(instrument: InstrumentLayout, groups: dict[int, list[int]], group_names: dict[int, str] | None = None, initial_preset_name: str | None = None, forward_group: int = 1, backward_group: int = 2, excluded_detectors: list[int] | None = None, projections: list[dict] | None = None, field_direction: str | None = None, parent: QWidget | None = None)[source]
Bases:
QDialogVisual detector grouping editor.
- Parameters:
instrument – Pre-selected instrument layout. Users can override this via the instrument combo inside the dialog.
groups – Current group definitions mapping group ID (1-based) to list of 1-based detector IDs.
group_names – Optional mapping from group ID to human-readable group name.
forward_group – Group ID currently designated as the forward group.
backward_group – Group ID currently designated as the backward group.
field_direction – Applied-field geometry of the run (
"Transverse"/"Longitudinal"/"Zero field"/None). When the run is transverse-field but the current preset is not the recommended transverse one, the dialog shows a non-blocking hint and pre-selects the recommended preset in the combo (the user still clicks Apply Grouping).parent – Parent Qt widget.
- __init__(instrument: InstrumentLayout, groups: dict[int, list[int]], group_names: dict[int, str] | None = None, initial_preset_name: str | None = None, forward_group: int = 1, backward_group: int = 2, excluded_detectors: list[int] | None = None, projections: list[dict] | None = None, field_direction: str | None = None, parent: QWidget | None = None) None[source]
- eventFilter(watched, event) bool[source]
Highlight a group’s detectors in the schematic while its row is hovered.
- get_result() dict[str, Any][source]
Return the editing result as a plain dictionary.
- Returns:
Keys:
"groups"Mapping from group ID (int, 1-based) to list of 1-based detector IDs. Only non-empty groups are included.
"group_names"Mapping from group ID (int, 1-based) to name string.
"forward_group"Group ID of the designated forward group.
"backward_group"Group ID of the designated backward group.
"instrument"Instrument name (str).
- Return type:
- staticMetaObject = PySide6.QtCore.QMetaObject("DetectorLayoutDialog" inherits "QDialog": )
Widgets
Detector schematic
- class asymmetry.gui.widgets.detector_schematic.DetectorSchematicWidget(instrument: InstrumentLayout, parent: QWidget | None = None)[source]
Bases:
QWidgetInteractive matplotlib-backed detector schematic.
- Parameters:
instrument – Instrument layout to render.
parent – Parent Qt widget.
Signals
-------
detector_toggled(int – Emitted when the user clicks a detector segment. Arguments are the 1-based
detector_idandTrueif the detector is now included in the active group,Falseif it has been removed.bool) – Emitted when the user clicks a detector segment. Arguments are the 1-based
detector_idandTrueif the detector is now included in the active group,Falseif it has been removed.
- detector_toggled: Signal
- detector_exclusion_toggled: Signal
(1-based detector id, now_excluded).
- Type:
Emitted in exclude mode
- __init__(instrument: InstrumentLayout, parent: QWidget | None = None) None[source]
- set_active_group(group_id: int) None[source]
Change which group is currently active (receives click toggles).
- Parameters:
group_id – 1-based group identifier.
- set_group_detectors(group_id: int, detector_ids: set[int]) None[source]
Set detector membership for one group and refresh the display.
- Parameters:
group_id – 1-based group identifier.
detector_ids – Set of 1-based detector IDs belonging to the group.
- set_all_groups(groups: dict[int, list[int]], active_group: int) None[source]
Replace all group assignments and set the active group.
- Parameters:
groups – Mapping from group ID (1-based) to list of 1-based detector IDs.
active_group – Group ID that should be considered active.
- set_excluded_detectors(detector_ids: set[int]) None[source]
Mark detectors (1-based ids) as excluded and refresh the display.
- set_exclude_mode(enabled: bool) None[source]
Toggle click-to-exclude mode (clicks edit the exclusion set).
- set_group_names(group_names: dict[int, str]) None[source]
Set the gid -> display-name mapping used to label hover tooltips.
Purely cosmetic (tooltip text only); does not affect group membership or colour. The layout dialog calls this whenever its name-edit fields change so hovering shows “Forward” rather than “Group 1”.
- set_group_highlight(group_id: int | None) None[source]
Temporarily emphasise group_id’s detectors (edge highlight).
Used by the layout dialog to highlight a group’s detectors in the schematic when the user hovers that group’s row. Pass
Noneto clear the highlight.
- set_instrument(instrument: InstrumentLayout) None[source]
Replace the displayed instrument layout.
- Parameters:
instrument – New instrument layout to render.
- staticMetaObject = PySide6.QtCore.QMetaObject("DetectorSchematicWidget" inherits "QWidget": Methods: #33 type=Signal, signature=detector_toggled(int,bool), parameters=int, bool #34 type=Signal, signature=detector_exclusion_toggled(int,bool), parameters=int, bool )