EventAnalysisController

class EventAnalysisController(available_subclasses: Optional[Mapping[str, List[str]]] = None, **kwargs)

Bases: MetaEventTabController

Subclass of MetaEventTabController for for managing event analysis view-model logic.

Connects the EventAnalysisModel and EventAnalysisView.

Public Methods

EventAnalysisController.load_event_plot(loader: str, eventfitter: str, channel: int, events: List[int], data_filter: str, raw: bool) → None

Load the selected events, their raw traces and their fits, ready to plot.

Eight calls and the assembly that turns their answers into plot arguments, all on this side: it is pure data marshalling - no Qt and no matplotlib - so none of it belongs in the widget. _update_event_plot stays in the View.

The alignment this builds is the load-bearing part. event_data and labels take one to three entries per event - the data, then optionally the raw trace, then optionally the fit - while the six feature lists take exactly one placeholder per event, and an event’s features are written to its own placeholder. So event_data is routinely longer than vertical_lines, and num_events counts events rather than traces. Getting that wrong attaches a fit’s features to another event’s subplot, which no gate would notice.

Three different unpacking rules, which the bus used to apply implicitly from each callee’s declared return type. load_event returns a dict whose data key is the samples; get_fitted_event returns a bare array and must not be unwrapped; get_plot_features returns a six-tuple in the order (vertical, horizontal, points, vlabels, hlabels, plabels). All three are explicit here.

One call hoisted. get_eventfitting_status takes only the channel, so the View asking it once per event asked the same question N times. It is asked once per request now, as the samplerate was on RawData’s trace path.

Parameters:
  • loader (str) – the event loader plugin’s key

  • eventfitter (str) – the event fitter plugin’s key, or “No Event Fitter”

  • channel (int) – the single channel whose events are being plotted

  • events (List[int]) – the event indices the user selected

  • data_filter (str) – the filter plugin’s key, or “” for no filtering

  • raw (bool) – whether to overlay each event’s unfiltered trace

Returns:

None

Return type:

None

EventAnalysisController.request_fitting_statuses(eventfitter: str, channels: List[int], data_filter: str) → None

Ask the fitter which channels it has already fitted, for the View to confirm.

The first half of the fitting launch, and the analogue of RawDataController.request_eventfinding_statuses: every channel’s status is resolved here, in one call. Asking per channel and reading each answer back off the widget is how the “start over?” prompt gets shown, or skipped, for the wrong channel - a look-up that fails leaves the previous channel’s fitted-ness in place.

Parameters:
  • eventfitter (str) – the event fitter plugin’s key

  • channels (List[int]) – the channels the user asked to fit

  • data_filter (str) – the filter plugin’s key, or “” for no filtering

Returns:

None

Return type:

None

EventAnalysisController.request_loader_channels(loader: str) → None

Fetch an event loader’s channel list and hand it to the View.

The direct analogue of RawDataController.request_reader_channels, against MetaEventLoader rather than MetaReader. A loader that cannot be read leaves the channel combobox alone rather than clearing it - an empty combobox reads as “this loader has no channels”, which is a different and more alarming thing than “this loader could not be read”.

Parameters:

loader (str) – the event loader plugin’s key

Returns:

None

Return type:

None

EventAnalysisController.start_fitting(eventfitter: str, channels: List[int], data_filter: str) → None

Fit the approved channels and run the resulting generators.

silent and indices are passed explicitly as False and None because the View always did, even though both match their defaults: stating them means this call does not change meaning if a default ever does.

A channel that cannot be launched is reported and skipped, and the ones that did register still run, for the same reason as write_events.

Parameters:
  • eventfitter (str) – the event fitter plugin’s key

  • channels (List[int]) – the channels the user approved

  • data_filter (str) – the filter plugin’s key, or “” for no filtering

Returns:

None

Return type:

None

EventAnalysisController.write_events(writer: str, channels: List[int]) → None

Hand each channel’s fitted events to a database writer and run the generators.

The analogue of RawDataController.commit_events, against MetaDatabaseWriter.write_events. Like that one it was never an emit-then-read: the plugin returns a generator and the bus passed it straight into set_generator as an argument, so there was no attribute to park it on and no stale value to inherit.

The same deliberate behaviour change applies. The View wrapped the whole loop in except (IndexError, ValueError) and skipped run_generators entirely if it fired - a guard that could not catch a plugin failure, because the bus swallowed those first. Now that call() raises, a channel that cannot be written is reported and skipped and the rest still run, rather than an arbitrary writer exception escaping a Qt slot.

Parameters:
  • writer (str) – the database writer plugin’s key

  • channels (List[int]) – the channels whose events are being written

Returns:

None

Return type:

None

Private Methods

EventAnalysisController._init() → None

Build this tab’s View and Model, and do any other setup the tab needs.

Assigning both self.view and self.model is required rather than conventional: the constructor calls this and then immediately connects the two to each other, so leaving either unset raises AttributeError before the tab exists. kwargs given to the base constructor are available as attributes by the time this runs.

EventAnalysisController._setup_connections() → None

Set up any local connections between the subordinate view and model