ProteinController

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

Bases: MetaSubsetTabController

Subclass of MetaSubsetTabController for managing protein view-model logic.

Relays queries, event data, and filter/column metadata between the database backend and ProteinView.

Public Methods

ProteinController.build_ensemble_histogram(loader: str, sql_filter: str, experiments_and_channels: Dict[str, List[int | None]] | None, plot_type: str, bins: Any, sizes: bool, dataset_label: str, dataset_key: Tuple[Any, ...], d: float, L: float, N: int) → None

Fetch one event subset and average it into a single histogram to draw.

A request slot. The aggregation happens with the fetch rather than in the widget: handing the widget a generator to walk means whole events, and the DataFrame built from them, live above the Model for no one’s benefit.

The drawing context arrives and departs unchanged; this slot marshals and does not interpret it. Nothing is handed back at all if the subset has no usable event, which is what leaves the previous figure in place.

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

  • sql_filter (str) – the subset filter’s WHERE-clause body, empty for all rows

  • experiments_and_channels (Optional[Dict[str, List[Optional[int]]]]) – the experiment and channel scope, or None

  • plot_type (str) – ‘Raw Histogram’ or ‘Filtered Histogram’

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin width (True) or a count (False)

  • dataset_label (str) – the label the histogram is drawn under

  • dataset_key (Tuple[Any, ...]) – the plotted-datasets key, handed back unchanged

  • d (float) – the diameter of the pore in nanometers

  • L (float) – the length of the pore in nanometers

  • N (int) – target number of samples to draw for each ensemble

Returns:

None

Return type:

None

ProteinController.check_for_existing_fit_columns(loader: str) → None

Ask whether this database already holds fit columns, and tell the View.

Phase one of a two-phase commit. _commit_fits interleaved a plugin call with a modal question for the user, which no single intent can express: the answer to “does this already exist?” decides whether the user is asked at all. RawDataController._start_eventfinder has the same shape for the same reason.

The table name is the answer and the flag - None means no such column, so nothing needs overwriting.

Parameters:

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

Returns:

None

Return type:

None

ProteinController.commit_fits(loader: str, fit_data: DataFrame, units: List[str | None], overwrite_table: str | None) → None

Drop any fit columns being replaced, then write the new ones.

Phase two, reached either straight away when the database holds no fit data or once the user has approved the overwrite. The user’s decision travels as overwrite_table rather than being held on the View between the two halves, which is what _start_eventfinder’s filter key does.

The DROP and DELETE statements are built here rather than in the widget, for the same reason as every other piece of SQL: writing it is not a widget’s job.

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

  • fit_data (pd.DataFrame) – the fitted columns to write, keyed by event id

  • units (List[Optional[str]]) – one unit per written column, None where dimensionless

  • overwrite_table (Optional[str]) – the table holding fit columns to drop first, or None

Returns:

None

Return type:

None

ProteinController.filter_xyerr_scatterplot(columns: Sequence[ndarray[tuple[int, ...], dtype[float64]]], log_flags: Sequence[bool], ax: Axes, axis_labels: Sequence[str], dataset_label: str) → None

Filter an error-bar scatterplot’s columns, and hand them back to draw.

Separate from the shared MetaSubsetTabController.filter_scatterplot because only this tab draws error bars. The four arrays go down together so one mask covers them all: a row dropped from the values has to be dropped from their error bars, or the bars no longer describe the points they sit on.

Parameters:
  • columns (Sequence[npt.NDArray[np.float64]]) – the raw x, y, x error and y error values

  • log_flags (Sequence[bool]) – log-scale each column? the two error columns never are

  • ax (Axes) – the axis object the View will draw on

  • axis_labels (Sequence[str]) – the axis labels, already formatted

  • dataset_label (str) – string to label the dataset

Returns:

None

Return type:

None

ProteinController.fit_distribution_events(event_data: Sequence[Dict[str, Any]], plot_type: str, bins: Any, sizes: bool, d: float, L: float, N: int) → None

Bin and fit every event on the individual distribution path, and hand back.

A request slot, the same shape as fit_event_histograms. The events and the pore geometry pass straight through; this slot marshals and does not interpret them.

Parameters:
  • event_data (Sequence[Dict[str, Any]]) – the events being plotted, passed back to the View unchanged

  • plot_type (str) – ‘Raw Histogram’ or ‘Filtered Histogram’

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin width (True) or a count (False)

  • d (float) – the diameter of the pore in nanometers

  • L (float) – the length of the pore in nanometers

  • N (int) – target number of samples to draw for each ensemble

Returns:

None

Return type:

None

ProteinController.fit_ensemble_geometry(bins: ndarray[tuple[int, ...], dtype[float64]], amplitude: ndarray[tuple[int, ...], dtype[float64]], plot_data: DataFrame, plot_type: str, d: float, L: float, N: int) → None

Fit the ensemble histogram, and hand the result back to the View to draw.

A request slot, the same shape as ClusteringController.cluster: the View asks, this calls the Model, and the answer goes back through a setter.

The context arrives and departs unchanged rather than being held here or on the View between the halves. A fit that fails its sanity checks is not an error - (None, None) is a legitimate answer the View reports to the user - so only an unexpected failure is caught, and it is reported rather than raised, because Qt invoked this from a signal and nothing above it could handle it.

Parameters:
  • bins (npt.NDArray[np.float64]) – the histogram’s bin centers

  • amplitude (npt.NDArray[np.float64]) – the amplitude in each bin

  • plot_data (pd.DataFrame) – the frame the View will draw the fit into

  • plot_type (str) – the plot type label to reuse when plotting the fit

  • d (float) – the diameter of the pore in nanometers

  • L (float) – the length of the pore in nanometers

  • N (int) – target number of samples to draw for each ensemble

Returns:

None

Return type:

None

ProteinController.fit_event_histograms(event_data: Sequence[Dict[str, Any]], plot_type: str, bins: Any, sizes: bool) → None

Bin and fit every event in one call, and hand the results back to draw.

A request slot. One call rather than one per event keeps each answer off the widget; see ProteinModel.fit_histograms. The binning happens ahead of the fitting in the same call, so the two describe the same bins and the widget is handed the histograms rather than building them.

The events pass straight through: this slot marshals, it does not interpret them. A failure is reported on the status panel rather than raised, because Qt invoked this from a signal and nothing above it could handle it - and an unusable bin request is now reported once here rather than logged once per event and drawn as a grid of empty subplots.

Parameters:
  • event_data (Sequence[Dict[str, Any]]) – the events being plotted, passed back to the View unchanged

  • plot_type (str) – ‘Raw Histogram’ or ‘Filtered Histogram’

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin width (True) or a count (False)

Returns:

None

Return type:

None

ProteinController.load_event_distribution_data(loader: str, sql_filter: str, experiments_and_channels: Dict[str, List[int | None]] | None) → None

Build the query for one subset, load its events, and hand both to the View.

Both distribution modes - individual and ensemble - run this same chain, so there is one slot rather than one per mode.

The ``_raw`` branch this replaced could never work, which is why it is gone rather than converted. It handed a complete SELECT to load_event_data as its conditions argument, and that argument is a WHERE-clause body: the loader spliced it in after WHERE, SQLite rejected the result as a syntax error, construct_event_data_query returned ("", debug) and the generator yielded nothing. Measured against a real loader - the same filter as an ordinary WHERE body yields every event, and as a raw SELECT yields none. Raw filters are refused before a plot is attempted now; see MetaSubsetTabView._refuse_raw_filters.

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

  • sql_filter (str) – the subset filter’s WHERE-clause body, empty for all rows

  • experiments_and_channels (Optional[Dict[str, List[Optional[int]]]]) – the scope the filter is built against

Returns:

None

Return type:

None

Private Methods

ProteinController._init() → None

Initialize the protein view and model.

ProteinController._setup_connections() → None

Connect internal view signals to their corresponding controller slots.

Returns:

None

Return type:

None