RawDataController

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

Bases: MetaEventTabController

Subclass of MetaEventTabController for managing raw data view-model logic.

Handles raw data plotting and PSD logic.

Public Methods

RawDataController.calculate_psd(psd_data: list, samplerate: float) → None

Calculate the Power Spectral Density (PSD) from the provided signal data and update the view.

Parameters:
  • psd_data (list) – List of time-domain signal arrays for which PSD will be computed.

  • samplerate (float) – Sampling rate of the signal in Hz.

RawDataController.commit_events(writer: str, channels: List[int], overwrite: bool) → None

Hand each channel’s found events to a writer and run the resulting generators.

The plugin returns a progress generator rather than a value, so it goes straight to set_generator and there is nothing to park on the widget in between.

One deliberate behaviour change. The View wrapped the whole loop in except (IndexError, ValueError) and skipped run_generators entirely if it fired. That guard could not catch a plugin failure, because the bus swallowed those before they reached it - and now that call() raises, an arbitrary exception from a writer would escape a Qt slot, which is worse than either. So a channel that cannot be committed is reported and skipped, and the channels that did register still run. Losing one channel’s commit is better than losing all of them.

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

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

  • overwrite (bool) – replace any of these channels the output already holds

Returns:

None

Return type:

None

RawDataController.compute_baseline_stats(data: List[Any], time_bases: List[Any], channels: List[int], start: Any) → None

Fit each channel’s baseline and hand the results back for plotting.

A request slot, the same shape as calculate_psd below: the fitting is RawDataModel’s, and the View plots on the answer.

A channel whose fit fails contributes None rather than aborting the plot, which is what the View did when it computed these itself - a flat or degenerate trace should still be drawn, just without its baseline band.

The division by 1000 converts pA to nA, matching the scale update_plot draws on. It moved here with the call it belongs to.

Parameters:
  • data (List[Any]) – one array of samples per channel

  • time_bases (List[Any]) – one time axis per channel, carried from the request because the samplerate that built it is not in scope here

  • channels (List[int]) – the channel identifiers, index-aligned with data

  • start (Any) – the start time the View is plotting from, passed straight through

Returns:

None

Return type:

None

RawDataController.load_event_plot_data(eventfinder: str, channel: int, events: List[int], data_filter: str) → None

Check the finder, bound the indices, resolve the filter, and load each event.

Five calls in a fixed order - status, then count, then the filter callable, then the samplerate, then one load per event - because each answer gates the next question. The status and count are asked even when no indices are selected, since they decide whether there is anything to select from.

Four stale reads disappear with the emits. Every one of those answers used to be parked on a View attribute written only on success and never cleared before the emit, so a dispatch failure that _dispatch_to swallowed left the previous value in place: the previous channel’s finished-ness, the previous channel’s event count - which then bounded this channel’s indices - the previous samplerate, and the previous event’s samples. call() raises, so each failure now stops the thing it should stop.

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

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

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

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

Returns:

None

Return type:

None

RawDataController.load_psd_data(reader: str, channels: List[int], start: float, length: float, data_filter: str) → None

Load and optionally filter a trace, then hand it back for the PSD.

Same loading as load_trace_data; only the tail differs, which is why the two intents are separate signals rather than one carrying a mode flag.

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

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

  • start (float) – start time in seconds

  • length (float) – duration in seconds

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

Returns:

None

Return type:

None

RawDataController.load_trace_data(reader: str, channels: List[int], start: float, length: float, data_filter: str, baseline: bool) → None

Load and optionally filter a trace, then hand it back for plotting.

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

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

  • start (float) – start time in seconds

  • length (float) – duration in seconds

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

  • baseline (bool) – whether the user asked for the baseline band

Returns:

None

Return type:

None

RawDataController.request_commit_statuses(writer: str, channels: List[int]) → None

Ask the writer which channels its output already holds, for the View to confirm.

The first half of a commit, shaped like request_eventfinding_statuses. The writer’s own experiment name and output file travel back with the answers, so the View can name a conflicting experiment and the file without holding anything between the two halves. A channel whose status cannot be read is dropped rather than guessed at, so it is never committed on a wrong assumption.

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

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

Returns:

None

Return type:

None

RawDataController.request_eventfinding_statuses(eventfinder: str, channels: List[int], data_filter: str) → None

Ask the finder which channels it has already completed, for the View to confirm.

The first half of the event-finding launch: every channel’s status is resolved here, in one call, and the View prompts on the answers. 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 finished-ness in place.

A channel whose status cannot be read is dropped rather than guessed at, and the remaining channels still get their prompt. data_filter is carried through untouched so the View does not have to hold it between the two halves.

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

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

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

Returns:

None

Return type:

None

RawDataController.request_reader_channels(reader: str) → None

Fetch a reader’s channel list and hand it to the View.

A reader that cannot be read leaves the channel combobox alone rather than clearing it - an empty combobox reads as “this reader has no channels”, which is a different and more alarming thing than “this reader could not be read”.

Parameters:

reader (str) – the reader plugin’s key

Returns:

None

Return type:

None

RawDataController.start_eventfinding(eventfinder: str, channel_ranges: List[Tuple[int, List[Tuple[float, float]]]], data_filter: str) → None

Launch the finder over the approved channels and run the resulting generators.

The second half. chunk_length is passed explicitly as 1.0 because the View always did, even though it is also the parameter’s default - keeping it makes the move visibly behaviour-preserving rather than relying on the default not changing.

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

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

  • channel_ranges (List[Tuple[int, List[Tuple[float, float]]]]) – each approved channel with the ranges to search

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

Returns:

None

Return type:

None

RawDataController.update_available_plugins(available_plugins: dict) → None

Resolve every event finder’s channels, then push the registry down as usual.

The finders are resolved here and handed down as a ready-made map, rather than each being looked up from inside RawDataView.update_available_plugins - a round trip nested inside a method the Controller is already running is the hardest place for a failure to be reported from.

The channels go down before the names, and that ordering is deliberate. Populating a combobox fires a selection change synchronously, which is what made the plugin-instance push order load-bearing earlier on this branch. Checked here rather than assumed: the event-finder combobox emits the action name parameter_changed, which lands in _handle_other_actions and reaches nothing that reads analysis_time_limits, so this order is defensive today rather than load-bearing. It costs nothing and it is the order that stays correct if a future handler does read that state.

Parameters:

available_plugins (dict) – dict of lists keyed by MetaClass, listing the identifiers of all instantiated plugins throughout the app.

Returns:

None

Return type:

None

Private Methods

RawDataController._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.

RawDataController._setup_connections() → None

Set up any local connections between the subordinate view and model