spacr.qt.dnd_handlers

Per-module drop handlers.

Each pipeline app has different expectations for what a “source” means. This module encodes those policies as DropHandler subclasses that the AppScreen wires up at construction time.

Handler map (also read by get_handler):

App

Accepts

mask measure external_masks annotate classify make_masks map_barcodes umap ml_analyze regression

recruitment activation analyze_plaques train_cellpose cellpose_masks cellpose_all

other modules

folder w/ images (auto-parses regex + preview) folder named merged OR one containing merged/ mixed image/label files or folders; assignment table folder with measurements/measurements.db folder with data/ or measurements/ image files and/or folders with images; one queue folder with FASTQ; also a raw .fastq.gz drop folder with measurements/measurements.db ditto ditto — the database attaches to a PLATE ROW; the sweep card also takes score / gRNA count CSVs folder with per-well recruitment CSVs folder with saved activation maps or the CV model dir plaque images and/or folders of them; a PDF (Figure) folder with image+mask pairs folder with images ditto — the “Mask the whole folder” key, kept so a folder dropped under it reads as images and not as a bare source path an existing source folder or supported data file

Every handler falls back to CSV settings-import via spacr.qt.dnd so users can also drop a settings CSV on any screen to load it.

Classes

AlignDropHandler

Use a dropped image folder (or one tile) as Align & Stitch input.

AnnotateDropHandler

Accept a plate folder with measurements/measurements.db or

BatchDropHandler

Load queue files or add dropped settings snapshots as jobs.

CellposeFolderDropHandler

Accept one folder with images, for the Cellpose training screens.

ClassifyDropHandler

Accept a plate folder with measurements/measurements.db or

CoefficientsDropHandler

Prediction Profiler: the regression coefficients under results/.

ConvertDropHandler

Use a dropped microscopy container or folder as converter input.

DataManagerDropHandler

Set the project in Data Manager, then measure it.

DatabaseDropHandler

Open a dropped measurements database in the Database Browser.

EvaluationBundleDropHandler

Classifier Evaluation: the run folder holding the evaluation bundle.

ExplainCvInputsDropHandler

Fill Explain CV's database or prediction input from one drop.

ExternalMasksDropHandler

Append mixed intensity images and external label masks to the mapper.

ForeignProjectDropHandler

Populate Import Project from image folders, tables and mapping files.

ImageFieldsDropHandler

Image-folder input shared by Model Compare.

ImageImportDropHandler

Point Import Images at a dropped folder, or at a file's folder.

InvestigateHitInputsDropHandler

Fill Investigate Hit's provenance inputs without guessing a hit.

LabelMaskDropHandler

Curate and Napari Bridge: one label mask, from wherever you drop.

LayerStackDropHandler

Layer Viewer: the dropped array, added as an image or as labels.

LayoutDropHandler

Drop policy for a screen that names what it wants, not where it is.

LineageDropHandler

Lineage: a database path field and one load.

MakeMasksDropHandler

Accept image files, folders of images, or both; one drop, one queue.

MapBarcodesDropHandler

Accept a FASTQ file (.fastq/.fastq.gz) or a folder

MaskDropHandler

Accept a folder of raw microscopy images and preview its filename

MeasureDropHandler

Accept the merged folder produced by the mask module, or a

MeasurementsDropHandler

Accept a database, its measurements folder, or its plate folder.

MethodsSourcesDropHandler

Methods & Results: fill whichever of its four source fields fits.

ModelZooDropHandler

Scan checkpoints, or use image-only folders as benchmark fields.

PlaqueDropHandler

Plaque Assay's own drop policy and its own words.

PlateQueueDropHandler

Queue plate folders that carry spaCR settings snapshots.

ProjectFolderDropHandler

A screen that takes a whole project, wherever inside it you drop.

ProjectRootsDropHandler

Project Browser: several folders at once, each becoming a root.

RegressionDropHandler

Regression's drop: a database to a plate row, CSVs to the sweep card.

ReportDropHandler

Accept a completed spaCR run folder and scan its report inputs.

ResultsDatabaseDropHandler

Database input for Plate Viewer and Annotator Agreement.

ResultsFolderDropHandler

Hit List: the results/ folder a regression wrote.

RunHistoryDropHandler

Run History: select the run a dropped run folder belongs to.

ScatterTableDropHandler

Image Scatter: a path field, a table picker, then the read.

SourceDropHandler

General source-path policy for modules without a narrower contract.

SubmissionSettingsDropHandler

Distributed Jobs: a settings snapshot to submit, or the plate with one.

SweepInputsDropHandler

Route dropped CSVs into Parameter Sweep's score and count lists.

TableDropHandler

A screen that reads one table: the explorers, the plotters, the gates.

TrainingRunsDropHandler

Accept a directory and asynchronously scan it for training runs.

Functions

active_scan_jobs(→ int)

How many folder-scan threads screen still owns.

forget_decisions(→ None)

Drop every cached drop decision, so the next one asks again.

get_handler(→ spacr.qt.dnd.DropHandler)

Return a fresh DropHandler for app_key.

plaque_inputs(→ Tuple[List[pathlib.Path], ...)

What a drop or a src holds for each Plaque Assay mode.

plaque_others(→ int)

How many dropped files Plaque Assay leaves aside.

plaque_selection_folder(→ pathlib.Path)

A folder that lists the dropped plaque images, without copying them.

scan_folder_structure(→ Dict[str, Any])

Walk a dropped folder ONCE and return the folder-metadata report.

scan_is_busy(→ bool)

True while a dropped folder is still being walked for screen.

scan_mask_container(→ Dict[str, Any])

Read a dropped container's header and plan its extraction. Worker-safe.

scan_mask_drop(→ Dict[str, Any])

Everything a mask drop asks the filesystem about one path. Worker-safe.

scan_mask_folder(→ Dict[str, Any])

List the top level of a dropped folder once. Worker-safe.

table_names(→ List[str])

Return the tables in a SQLite file, or [] for anything else.

Module Contents

class spacr.qt.dnd_handlers.AlignDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Use a dropped image folder (or one tile) as Align & Stitch input.

apply(path: pathlib.Path, screen) → None[source]

Point src at the folder. A dropped FILE gives its parent, because stitching one tile is not a thing you can ask for.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A folder holding image tiles, or one tile file.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name the unit Align works in: a folder of tiles, not one image.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.AnnotateDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a plate folder with measurements/measurements.db or the .db file itself.

apply(path: pathlib.Path, screen) → None[source]

Resolve a dropped database to the plate folder that owns it.

TWO LEVELS UP, BUT ONLY FROM measurements/. The canonical layout is <plate>/measurements/measurements.db, so climbing two levels finds the plate – but can_accept also allows a loose db, and climbing blindly from one of those would name a folder that has nothing to do with it.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A db file, or a plate folder holding measurements/measurements.db.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name the file’s path AND which module writes it, so a user who has not run Measure yet learns what is missing rather than that they are wrong.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.BatchDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Load queue files or add dropped settings snapshots as jobs.

accepts_multiple() → bool[source]

Yes: a batch is many jobs, so many drops is the natural gesture.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Load a saved queue, or add each settings snapshot as a job.

A QUEUE REPLACES, SNAPSHOTS ADD. A JSON or YAML file IS the queue and loading it is the whole gesture; a CSV or a folder contributes jobs to the queue that is already there.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A saved queue, a settings CSV, or a folder of settings snapshots.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name all three shapes; only the first is guessable from the name.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.CellposeFolderDropHandler[source]

Bases: MakeMasksDropHandler

Accept one folder with images, for the Cellpose training screens.

Their src is a folder the run lists, so a file is refused here even though Make Masks itself takes one.

accepts_multiple() → bool[source]

One folder is one source.

Returns:

False.

apply_all(paths: Sequence[pathlib.Path], screen) → bool[source]

Decline: each folder is set as src by apply().

Parameters:
  • paths – the accepted folders.

  • screen – the screen to wire the drop into.

Returns:

False.

can_accept(path: pathlib.Path) → bool[source]

A folder with images in it.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

The sentence the Cellpose screens have always shown.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.ClassifyDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a plate folder with measurements/measurements.db or a folder produced by the annotate step.

apply(path: pathlib.Path, screen) → None[source]

Add the plate to src, which Classify holds as a LIST.

APPENDED, NOT REPLACED. Classify compares plates, so a second drop that overwrote the first would make the comparison impossible to set up by the gesture the rest of the application uses for it.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A plate folder holding measurements, crops, or a training set.

THREE SHAPES BECAUSE THERE ARE TWO WORKFLOWS. Classify trains on measured features or on image crops, and the crops arrive either as data/ from Measure or as train/ from Annotate.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name all three layouts, since which one you have depends on which route through the application you took.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.CoefficientsDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

Prediction Profiler: the regression coefficients under results/.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Find the coefficients CSV the regression wrote, and load it.

A folder is searched rather than refused, because results/ is what a user has to hand and the file inside it has a name they did not choose and have no reason to remember.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.ConvertDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Use a dropped microscopy container or folder as converter input.

apply(path: pathlib.Path, screen) → None[source]

Set the source, normalising a dropped file to its folder.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder, or one microscopy container or image file.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name the container formats, because a user with an ND2 will not recognise themselves in the word ‘image’.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.DataManagerDropHandler(app_key: str = '')[source]

Bases: ProjectFolderDropHandler

Set the project in Data Manager, then measure it.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Set the project, then measure it.

The scan is the reason to drop a folder here at all, so it follows the set rather than waiting for a second gesture.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.DatabaseDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Open a dropped measurements database in the Database Browser.

apply(path: pathlib.Path, screen) → None[source]

Open the database, raising the screen’s own reason if it refuses.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Delegated, so the Browser and the measurement screens cannot disagree about what counts as a database.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name all three accepted shapes in the Browser’s own words.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.EvaluationBundleDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

Classifier Evaluation: the run folder holding the evaluation bundle.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

apply(path: pathlib.Path, screen) → None[source]

Prefer what the vocabulary resolved; fall back to the folder dropped.

The fallback matters: a run folder that the vocabulary cannot place is still the folder the user meant, and scanning it is more useful than refusing it.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A folder, or a file the vocabulary reads as a bundle directly.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

deliver(screen, value: str, target) → None[source]

Fill the source field, then scan it.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.ExplainCvInputsDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Fill Explain CV’s database or prediction input from one drop.

accepts_multiple() → bool[source]

Yes: the database and the predictions are two files, dropped together.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Place path in Explain CV’s database or prediction control.

Parameters:
  • path – accepted database, project directory, or prediction CSV.

  • screen – host screen exposing the explain input panel.

can_accept(path: pathlib.Path) → bool[source]

Return whether path is an Explain CV database or CSV input.

Parameters:

path – database, project directory, or prediction CSV to test.

error_message(path: pathlib.Path) → str[source]

Explain why path is not an Explain CV input.

Parameters:

path – rejected file or directory.

class spacr.qt.dnd_handlers.ExternalMasksDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Append mixed intensity images and external label masks to the mapper.

accepts_multiple() → bool[source]

Yes: images and their masks are separate files and arrive together.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Append to the inputs list, and refuse loudly if the screen has none.

APPEND, NOT REPLACE. The mapper pairs images with masks, so a drop that replaced the list would undo the pairing the previous drop just contributed to.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder, or an image/mask file in a label-bearing raster format.

No distinction between image and mask here: which is which is decided by the mapper the drop feeds, not by the file’s name.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name the raster formats, and say folders work, since a mask set is normally a folder rather than a file.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.ForeignProjectDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Populate Import Project from image folders, tables and mapping files.

accepts_multiple() → bool[source]

Yes: a foreign project is images plus a table plus, often, a mapping.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Route the drop to whichever of the module’s inputs it fits.

A CSV IS READ BEFORE IT IS PLACED. A mapping and a measurement table are both CSVs, and only the header says which – so the header is checked rather than the extension trusted.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder, an image, a measurement table, or a JSON mapping.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name all four kinds, because this module’s whole job is that the inputs did not come from spaCR and so have no expected shape.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.ImageFieldsDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Image-folder input shared by Model Compare.

apply(path: pathlib.Path, screen) → None[source]

Set the source, and raise with the screen’s own reason if it refuses.

The screen knows why it could not read the folder; repeating a generic sentence over the top of that would hide the useful half.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A folder holding microscopy fields, or one field file.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Say ‘fields’, which is the word this module’s screen uses.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.ImageImportDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Point Import Images at a dropped folder, or at a file’s folder.

A FOLDER IS THE UNIT, and a dropped file is normalised to the one holding it, because the module reads what VARIES ACROSS a folder to work out what the names mean. One file has no variance and would say nothing; the folder it came out of is what the user meant to hand over anyway.

A JSON file is a saved plan, not an acquisition: dropping one reloads the answers from a previous import, which is the gesture that makes next week’s plate one press.

apply(path: pathlib.Path, screen) → None[source]

Load a dropped plan, or point the module at the folder.

A json is a saved plan and reloads previous answers; anything else is normalised to its folder, which is the unit the module reads.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder, any image file, or a saved import plan.

Wider than the other handlers on purpose: this module is where a folder goes to be UNDERSTOOD, so refusing one for not looking like images yet would refuse the case it exists for.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name all three things it takes, since the third is not guessable.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.InvestigateHitInputsDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Fill Investigate Hit’s provenance inputs without guessing a hit.

accepts_multiple() → bool[source]

Yes: provenance is assembled from several files, not one.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Route path to the matching Investigate Hit control.

Parameters:
  • path – accepted database, results directory, prediction CSV, or guide-fraction CSV.

  • screen – host screen exposing the investigate input panel.

can_accept(path: pathlib.Path) → bool[source]

Return whether path can supply an Investigate Hit input.

Parameters:

path – database, directory, prediction CSV, or fractions CSV to test.

error_message(path: pathlib.Path) → str[source]

Explain why path is not an Investigate Hit input.

Parameters:

path – rejected file or directory.

class spacr.qt.dnd_handlers.LabelMaskDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

Curate and Napari Bridge: one label mask, from wherever you drop.

Dropping the project resolves masks/; a folder of masks is not one mask, so the file is asked for rather than guessed at.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Resolve the drop to ONE mask and hand it to whichever setter exists.

Curate and the Napari bridge name the act differently; a handler per spelling would be two copies of this policy that could disagree.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.LayerStackDropHandler(app_key: str = '')[source]

Bases: LabelMaskDropHandler

Layer Viewer: the dropped array, added as an image or as labels.

A viewer stacks layers, so a multi-drop of an image and its mask lands as two layers rather than as the first one.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

accepts_multiple() → bool[source]

Yes: a stack is layers, so several files at once is the gesture.

Returns:

True to be called once per item on a multi-drop.

deliver(screen, value: str, target) → None[source]

Add the array as labels or as an image, decided by where it came from.

A file out of masks/ is a label array; anything else is the image it belongs on. Guessing from the pixels instead would be wrong for a binary image and silent about it.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.LayoutDropHandler(app_key: str = '')[source]

Bases: spacr.qt.dnd.DropHandler

Drop policy for a screen that names what it wants, not where it is.

A subclass says two things: the vocabulary terms it consumes (kinds, from spacr.ports.ALL_KINDS) and what to do with the answer (deliver()). Everything between — climbing from the dropped path to the project root, asking the registry, falling back to the declared layout, noticing that the answer is ambiguous — is spacr.chaining.resolve_drop().

Ambiguity is routed through the machinery spacr.qt.dnd already has: an ambiguous drop reports can_accept() is False and returns the candidates from suggest_alternatives(), so the user gets the “did you mean…” chooser and apply() is called again with their answer. A drop that resolves to nothing reports spacr.chaining.DropResolution.reason, which is spacr.ports.check_ready()’s own sentence about what is missing.

Parameters:

app_key – which screen this handler belongs to, for the messages a refused drop shows. Empty falls back to the subclass’s label, and then to its class name – so a handler always has something to name itself with rather than reporting an empty string to the user.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

apply(path: pathlib.Path, screen) → None[source]

Deliver a direct hit, or resolve the folder and deliver what it names.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Accept a direct hit, or a folder the vocabulary resolves unambiguously.

AMBIGUOUS IS A REFUSAL, not a guess. A folder that could serve two of the kinds this screen asks for has no right answer here, and picking one would be wrong half the time and silent both times – suggest_alternatives offers the choices instead.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

abstract deliver(screen, value: str, target) → None[source]

Put value into the screen. Runs on the GUI thread.

Parameters:
  • screen – the screen the drop landed on.

  • value – the resolved path.

  • target – the spacr.chaining.DropTarget it came from, or None for a file the user dropped directly.

error_message(path: pathlib.Path) → str[source]

The resolver’s own reason, or a bare refusal when it could not read.

The resolver knows which kind was missing; a generic sentence over the top of that would replace the only informative half.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

resolve(path: pathlib.Path)[source]

Return the spacr.chaining.DropResolution for path.

Parameters:

path – dropped path to resolve against this handler’s app and artifact kinds.

suggest_alternatives(path: pathlib.Path) → List[pathlib.Path][source]

Every path the resolver considered, flattened for the ‘did you mean’ UI.

This is what makes an ambiguous refusal useful rather than a dead end.

Parameters:

path – the dropped file or folder.

Returns:

nearby paths that WOULD be accepted.

class spacr.qt.dnd_handlers.LineageDropHandler(app_key: str = '')[source]

Bases: TableDropHandler

Lineage: a database path field and one load.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Fill the database field, then load.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.MakeMasksDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept image files, folders of images, or both; one drop, one queue.

accepts_multiple() → bool[source]

Several files and folders in one drop make one queue.

Returns:

True.

apply(path: pathlib.Path, screen) → None[source]

Open the one path; a screen without a queue gets it as src.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

apply_all(paths: Sequence[pathlib.Path], screen) → bool[source]

Hand the whole drop to Make Masks, which queues it in drop order.

Parameters:
  • paths – the accepted files and folders, in drop order.

  • screen – the screen to wire the drop into.

Returns:

False for a screen that is not Make Masks, so each path goes through apply() as a source folder instead.

can_accept(path: pathlib.Path) → bool[source]

An image file, a .npy, or a folder with images or spaCR output.

A folder whose images sit only in subfolders, and a folder spaCR wrote (merged/*.npy, sorted_channels), are taken too, and a .npy so the screen can say what it is; spacr.drop_classification.classify_drop() decides what the drop means. Pairs are found later, not required here.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Say what the images are FOR – fine-tuning Cellpose – because the folder that is right for this module is not the one that is right for segmentation.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

suggest_alternatives(path: pathlib.Path) → List[pathlib.Path][source]

Nearby folders that do hold images, for the ‘did you mean’ prompt.

Parameters:

path – the dropped file or folder.

Returns:

nearby paths that WOULD be accepted.

class spacr.qt.dnd_handlers.MapBarcodesDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a FASTQ file (.fastq/.fastq.gz) or a folder containing one.

apply(path: pathlib.Path, screen) → None[source]

Point src at the folder and, for a dropped file, fastq at it.

TWO FIELDS FROM ONE DROP. Dropping the FASTQ itself is the precise gesture and fills both; dropping the folder leaves the file unset, because which of several reads was meant is not something to guess.

WHICH OF THE TWO IT IS COMES FROM THE NAME, not from a stat. The is_file() that stood here asked the filesystem a question the filename had already answered – on the GUI thread, immediately after can_accept had asked it too.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A FASTQ file, or a folder holding one.

Stops at the FIRST match rather than listing the folder: a sequencing run can hold thousands of files and the question is only whether there is one.

THE NAME IS ASKED BEFORE THE FILESYSTEM. The usual drop here is a .fastq.gz, and its name settles it, so the common case touches the disk not at all. Only a folder has to be listed, and that listing waits at most DECISION_BUDGET_S – dropping a sequencing folder from a sleeping network share is the exact gesture that was reported as “opening map barcodes crashes spacr”.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name both spellings of the extension, since gz is the common one and looks like a different kind of file.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.MaskDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a folder of raw microscopy images and preview its filename regex parse. Multi-drop is supported.

accepts_multiple() → bool[source]

Yes: several plates segmented in one gesture is the common case.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Set src, then read the folder and preview its filename parse.

The read is on a worker thread and the report renders when it returns, because a plate can hold thousands of names and the drop must not freeze the window while they are counted.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A folder with images at the top level, or one readable image file.

A CONTAINER FILE IS ASKED, NOT ASSUMED. describe_file decides whether a czi or nd2 can actually be read, because the suffix says what a file claims to be and the reader says what it is.

Answered from _facts, which is the ONE walk this drop pays for – the question above is exactly what scan_mask_drop already settled.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name the formats AND ‘at the top level’, which is the usual miss: a folder of per-well subfolders looks right and is not.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

suggest_alternatives(path: pathlib.Path) → List[pathlib.Path][source]

Nearby folders that DO hold images, for the ‘did you mean’ prompt.

Only for a folder: a file that is not an image has no near miss worth offering, and scan_mask_drop returns none for one.

Already found by the same scan that rejected the path. Searching the parent and the children for images is a walk of its own, and doing it here meant a rejected drop paid for a second one.

Parameters:

path – the dropped file or folder.

Returns:

nearby paths that WOULD be accepted.

class spacr.qt.dnd_handlers.MeasureDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept the merged folder produced by the mask module, or a parent folder that contains one.

apply(path: pathlib.Path, screen) → None[source]

Set src to the PLATE folder, not to merged/ inside it.

ONE STRING FOR ONE PLACE. This used to drill into merged, while auto-chaining filled the same field with the plate – so dropping a folder and letting the chain fill it produced two different strings for one plate, and settings files written the two ways did not match.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

The merged folder mask produced, a plate folder holding one, or a single mask/image file.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name merged explicitly, and say the plate folder works too.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

suggest_alternatives(path: pathlib.Path) → List[pathlib.Path][source]

Any merged/ folder one level down, for the ‘did you mean’ prompt.

Dropping the plate folder instead of the merged inside it is the mistake this exists to catch.

Parameters:

path – the dropped file or folder.

Returns:

nearby paths that WOULD be accepted.

class spacr.qt.dnd_handlers.MeasurementsDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a database, its measurements folder, or its plate folder.

Where the screen takes its inputs ONE ROW PER PLATE – today only Regression, through the paired_data table – the database is attached to a plate row instead of setting src. That is not an app-key special case: it follows the shape of the screen, so any panel that grows a per-plate input table gets it without a registry edit.

It matters because the two gestures used to disagree. Dropping measurements.db on the regression input table attaches it to a plate; dropping the same file two inches higher, on the screen around it, landed here and set src – a key the regression panel does not even display. The drop reported success and changed nothing the user could see.

apply(path: pathlib.Path, screen) → None[source]

Attach the database to a PLATE ROW, not to src.

This screen’s inputs are one row per plate, so the database belongs on the row it describes. src is not where its measurements live, and putting it there would leave the row empty and the run without input.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

The database, the measurements/ folder, or the plate folder above it.

All three name the same database, and which one a user has to hand depends on how far into the tree they happened to be looking.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

static database_file(path: pathlib.Path)[source]

The database path names: itself, or the one under it.

Returns None when there is no database file to be found, so a caller can fall back rather than hand a folder to something that expects to open a database.

Parameters:

path – database file, measurements directory, or plate directory to resolve.

error_message(path: pathlib.Path) → str[source]

Name the canonical path, which is the one a user can check.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.MethodsSourcesDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

Methods & Results: fill whichever of its four source fields fits.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

accepts_multiple() → bool[source]

Yes: the four sources are four separate things to drop.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Fill whichever of the four source fields this path fits.

SORTED BY WHAT THE PATH IS, not by drop order, so the same four files land in the same four fields however they are dropped.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Anything on disk: which of the four fields it fills is decided in apply, from what the path IS rather than from what was dropped.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

class spacr.qt.dnd_handlers.ModelZooDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Scan checkpoints, or use image-only folders as benchmark fields.

apply(path: pathlib.Path, screen) → None[source]

Scan checkpoints, or take the folder as benchmark fields.

THE CHEAP ANSWER FIRST. A dropped checkpoint, or one at the top level of the dropped folder, is one directory listing that stops at the first hit – and that is what a real model folder looks like, so the common drop stays fully synchronous.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder, a checkpoint file, or an image file.

THE FOLDER IS NOT INSPECTED HERE. Which of the two kinds it is – checkpoints or benchmark fields – is decided in apply, because deciding it twice would walk the directory twice.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name both kinds of folder this module takes.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.PlaqueDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Plaque Assay’s own drop policy and its own words.

It takes plaque images, folders of them, both mixed, and in Figure mode any number of PDFs; other files dropped with them are left aside quietly. Before this handler Plaque Assay was given Make Masks’ policy, which refused a folder it could not use with Make Masks’ sentence.

accepts_multiple() → bool[source]

Several images and folders in one drop make one selection.

Returns:

True.

apply(path: pathlib.Path, screen) → None[source]

Take one dropped path.

Parameters:
  • path – the dropped file or folder.

  • screen – the Plaque Assay screen.

apply_all(paths: Sequence[pathlib.Path], screen) → bool[source]

Point Plaque Assay at the drop: PDFs to Figure mode, images to src.

The mode follows what was dropped: PDFs, or a folder of them, dropped in Plaque mode ask to switch to Figure mode; images dropped in Figure mode ask to switch to Plaque mode; PDFs and images together say that PDFs are read in Figure mode and images in Plaque mode, and ask which to read. Staying in Figure mode reads the images as figures; staying in Plaque mode leaves the PDFs unread.

Parameters:
  • paths – the accepted files and folders, in drop order.

  • screen – the Plaque Assay screen.

Returns:

True; the drop is always handled here.

can_accept(path: pathlib.Path) → bool[source]

Any file, or a folder with plaque images or PDFs in it.

A file that is neither an image nor a PDF is taken so that apply_all() can leave it aside quietly when it was dropped with images or PDFs, and refuse it when it was dropped alone.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Say what Plaque Assay reads, in its own terms.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

suggest_alternatives(path: pathlib.Path) → List[pathlib.Path][source]

Nearby folders that hold plaque images.

Parameters:

path – the dropped file or folder.

Returns:

sibling and child folders with plaque images in them.

class spacr.qt.dnd_handlers.PlateQueueDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Queue plate folders that carry spaCR settings snapshots.

accepts_multiple() → bool[source]

Yes: queueing several plates in one gesture is the point.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Queue every plate the drop names.

TWO SHAPES, ONE GESTURE. A CSV is a plate LIST and each row becomes an item; a folder is ONE plate and each settings snapshot in it becomes an item.

A PARTIAL DROP IS REPORTED. One unreadable snapshot among several used to report plain success, so that plate quietly never reached the queue and the user found out when the run they expected was missing.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A plate-list CSV, or a plate folder holding settings snapshots.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name both shapes, and the src column the CSV form needs.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.ProjectFolderDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

A screen that takes a whole project, wherever inside it you drop.

kinds is empty on purpose: there is no port for “the project”, and inventing one would put it in the module graph. The layout walk still happens — dropping <plate>/measurements/measurements.db on the pipeline graph opens <plate>.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

can_accept(path: pathlib.Path) → bool[source]

A folder the resolver reads as one project, without ambiguity.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

deliver(screen, value: str, target) → None[source]

Call the first setter this screen actually offers.

SEVERAL SPELLINGS, ONE POLICY. Screens name the act of taking a project differently, and a handler per spelling would be copies of this policy that could drift about what a project is. A screen with none of them is a wiring error and says so rather than failing quietly.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.ProjectRootsDropHandler(app_key: str = '')[source]

Bases: ProjectFolderDropHandler

Project Browser: several folders at once, each becoming a root.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

accepts_multiple() → bool[source]

Yes: several roots at once is what the browser is for.

Returns:

True to be called once per item on a multi-drop.

deliver(screen, value: str, target) → None[source]

Add the root, treating an already-watched folder as a no-op.

add_root returns False for a root already listed. That is not a failure and must not be reported as one: dropping a folder the browser already watches should do nothing, not raise an error dialog.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.RegressionDropHandler[source]

Bases: MeasurementsDropHandler

Regression’s drop: a database to a plate row, CSVs to the sweep card.

Regression takes its measurements one row per plate, which is what MeasurementsDropHandler already does. It ALSO carries the parameter sweep as a card, and that card takes the sweep’s two CSV lists – per-object scores and gRNA counts – which a measurements handler rejects outright because they are not measurements.db.

Both halves have to answer at one drop target. The sweep no longer has a tile of its own, so the Regression screen is the ONLY place its inputs can be dropped; without this, a dropped sweep bundle got “needs a plate folder with measurements/measurements.db” and filled nothing.

The database shape is tried first: it is the screen’s own input, and a measurements.db is never one of the sweep’s CSV lists, so the two cannot compete for the same path.

accepts_multiple() → bool[source]

Yes. The sweep half takes many CSVs at once, which is the gesture the card exists for – one plate’s scores and counts arrive together.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Attach path to a plate row or the embedded sweep card.

Parameters:
  • path – accepted database, plate directory, sweep CSV, or sweep directory.

  • screen – Regression screen receiving the resolved input.

can_accept(path: pathlib.Path) → bool[source]

Return whether Regression can route path to either input area.

Parameters:

path – database, plate directory, sweep CSV, or sweep directory to classify.

error_message(path: pathlib.Path) → str[source]

Explain why path matches neither Regression input contract.

Parameters:

path – rejected file or directory.

class spacr.qt.dnd_handlers.ReportDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a completed spaCR run folder and scan its report inputs.

apply(path: pathlib.Path, screen) → None[source]

Set the source and scan it, raising the screen’s own reason on failure.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder: whether it holds a run is what the scan answers.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Say the folder must be a COMPLETED run, which is the usual mistake.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.ResultsDatabaseDropHandler[source]

Bases: DatabaseDropHandler

Database input for Plate Viewer and Annotator Agreement.

apply(path: pathlib.Path, screen) → None[source]

Open the database through whichever setter this screen offers.

TWO SPELLINGS, ONE HANDLER. Plate Viewer and Annotator Agreement name the same act differently, and a handler per spelling would be two copies of this policy that could disagree about what a database is.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

class spacr.qt.dnd_handlers.ResultsFolderDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

Hit List: the results/ folder a regression wrote.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Load the folder, normalising a dropped file to the folder holding it.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.RunHistoryDropHandler(app_key: str = '')[source]

Bases: ProjectFolderDropHandler

Run History: select the run a dropped run folder belongs to.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

apply(path: pathlib.Path, screen) → None[source]

Refresh, then select the run the dropped folder belongs to.

REFRESH FIRST. A run finished after this screen was opened is not in the list yet, and selecting it would fail for a folder that plainly exists – which reads as a bug rather than as a stale list.

The refusal names both ways out, because a run can be missing from the list either by not being there or by being filtered out of it.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Anything on disk; whether it is a known run is decided in apply.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

class spacr.qt.dnd_handlers.ScatterTableDropHandler(app_key: str = '')[source]

Bases: TableDropHandler

Image Scatter: a path field, a table picker, then the read.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Fill the path field, then read the source.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.SourceDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

General source-path policy for modules without a narrower contract.

Every standard AppScreen has a src field. Accepting a real directory here gives newer and less-specialised modules drag-and-drop support automatically instead of requiring a registry edit for every screen. A dropped file is only accepted for known data extensions and is normalised to its containing directory.

Where the module declares ports, the value comes from spacr.chaining.resolve_drop() — the same answer auto-chaining would fill the field with — so dropping <plate>/measurements and letting the chain fill src cannot produce two different strings. A module with no declaration keeps the plain normalisation below, which is all there is to go on.

apply(path: pathlib.Path, screen) → None[source]

Let the vocabulary place the drop, falling back to src.

A screen that declares ports gets them filled; one that declares none still gets its source folder set, which is the behaviour every screen had before ports existed.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder, or a data file in a format the standard screens read.

Deliberately broad: this is the FALLBACK policy, and refusing here would leave a screen with no drop behaviour at all rather than a generic one.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Generic on purpose – a fallback cannot name what it does not know.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.SubmissionSettingsDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

Distributed Jobs: a settings snapshot to submit, or the plate with one.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

apply(path: pathlib.Path, screen) → None[source]

Submit the snapshot, ASKING when the folder holds more than one.

A plate with several snapshots has no right answer, and picking the first would submit a run the user did not choose – expensively, and without saying which one it took.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

A settings snapshot, or a plate folder holding at least one.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Name both file spellings and the folder layout, since which one a user has depends on whether they saved a run or ran one.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

class spacr.qt.dnd_handlers.SweepInputsDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Route dropped CSVs into Parameter Sweep’s score and count lists.

The sweep takes the same two inputs the regression does, but holds them in two separate list widgets rather than one paired table, so the side has to be decided before the file is added. It is decided the same way Regression decides it – from the CSV header, via spacr.qt.widgets.file_list.side_for_header() – because a count table filed as a score is not an error the user sees, it is a wrong sweep.

A dropped FOLDER contributes the CSVs directly inside it, which is what makes “drop the plate folder” work for a screen whose whole point is running many plates at once.

accepts_multiple() → bool[source]

Yes: a plate’s scores and counts arrive together, as two CSVs.

Returns:

True to be called once per item on a multi-drop.

apply(path: pathlib.Path, screen) → None[source]

Route score and count CSVs from path to the sweep panel.

Parameters:
  • path – accepted CSV file or directory of CSV files.

  • screen – sweep panel or host screen carrying it on _sweep.

can_accept(path: pathlib.Path) → bool[source]

Return whether path contributes at least one sweep CSV.

Parameters:

path – CSV file or directory whose immediate CSV children are considered.

error_message(path: pathlib.Path) → str[source]

Explain why path cannot populate the sweep inputs.

Parameters:

path – rejected file or directory.

class spacr.qt.dnd_handlers.TableDropHandler(app_key: str = '')[source]

Bases: LayoutDropHandler

A screen that reads one table: the explorers, the plotters, the gates.

Drop the project and it finds measurements/measurements.db; drop the database and it uses it; drop a CSV and it reads that. When the database holds more than one table the table is asked rather than taken — load_path picks the first one silently, which is fine for a file dialog where the user chose the file and wrong for a drop where they chose a folder.

Create the handler, naming itself if the caller did not.

Parameters:

app_key – the module this handler serves; empty falls back to the handler’s label and then to its class name, so a handler always has something to report itself as.

deliver(screen, value: str, target) → None[source]

Ask which table, then load it.

A CANCELLED CHOOSER IS NOT A FAILURE. The user changed their mind about a drop; loading something anyway, or raising, would both be worse than doing nothing.

Parameters:
  • screen – the screen to wire the drop into.

  • value – the resolved path, as text.

  • target – the port the vocabulary matched, or None.

class spacr.qt.dnd_handlers.TrainingRunsDropHandler[source]

Bases: spacr.qt.dnd.DropHandler

Accept a directory and asynchronously scan it for training runs.

apply(path: pathlib.Path, screen) → None[source]

Start the scan, and raise with the screen’s own reason if it refuses.

Parameters:
  • path – the dropped file or folder.

  • screen – the screen to wire the drop into.

can_accept(path: pathlib.Path) → bool[source]

Any folder: what is in it is decided by the scan, not by the drop.

Parameters:

path – the dropped file or folder.

Returns:

True when this handler can use path as-is.

error_message(path: pathlib.Path) → str[source]

Say that runs live in a folder, not in a file.

Parameters:

path – the dropped file or folder.

Returns:

the sentence shown when the drop is refused.

spacr.qt.dnd_handlers.active_scan_jobs(screen) → int[source]

How many folder-scan threads screen still owns.

Parameters:

screen – screen whose active drop-scan jobs are counted.

spacr.qt.dnd_handlers.forget_decisions() → None[source]

Drop every cached drop decision, so the next one asks again.

spacr.qt.dnd_handlers.get_handler(app_key: str) → spacr.qt.dnd.DropHandler[source]

Return a fresh DropHandler for app_key.

Falls back to SourceDropHandler so every conventional AppScreen can at least receive its source folder.

Parameters:

app_key – registered built-in or plugin application key whose drop policy is requested.

spacr.qt.dnd_handlers.plaque_inputs(paths: Sequence[pathlib.Path], *, limit: int = 20000) → Tuple[List[pathlib.Path], List[pathlib.Path], List[pathlib.Path]][source]

What a drop or a src holds for each Plaque Assay mode.

Only the top level of a folder is read, as the plaque run reads it, and at most limit entries of each, so a huge folder on a slow share cannot hold the window for long.

Parameters:
  • paths – dropped files and folders, or [src].

  • limit – entries read per folder.

Returns:

(pdfs, images, paper_folders). A paper folder – one a paper was fetched into, holding its paper.json, legends.csv or text_layer.json – is Figure mode’s input, and its figure images are not counted as plaque images.

spacr.qt.dnd_handlers.plaque_others(paths: Sequence[pathlib.Path], *, limit: int = 20000) → int[source]

How many dropped files Plaque Assay leaves aside.

A file that is neither a plaque image nor a PDF, dropped or at the top of a dropped folder, is ignored rather than refused; hidden files are not counted, nor is what a paper folder holds.

Parameters:
  • paths – dropped files and folders, or [src].

  • limit – entries read per folder.

Returns:

the number of such files.

spacr.qt.dnd_handlers.plaque_selection_folder(images: Sequence[pathlib.Path], stamp: str | None = None) → pathlib.Path[source]

A folder that lists the dropped plaque images, without copying them.

A plaque run reads one folder and writes its masks beneath it, so a drop of loose files becomes a folder of links to them – symbolic, else hard – named plaque_selection_<time> beside the first image, or in the temporary folder when that one cannot be written. Two images with the same name from different folders keep both, the second under its folder’s name.

Parameters:
  • images – the images, in drop order.

  • stamp – the time part of the name; now when omitted.

Returns:

the folder.

Raises:

OSError – when an image can be neither linked nor listed.

spacr.qt.dnd_handlers.scan_folder_structure(path) → Dict[str, Any][source]

Walk a dropped folder ONCE and return the folder-metadata report.

Worker-safe: plain data in, plain data out, nothing Qt. The single walk is the point. This replaced three walks of the same tree — detect_folder_metadata did one, plan_folder_extraction did another and called detect_folder_metadata again for a third — all on the GUI thread, all inside the drop event.

The probe is pulled off the same lazy generator the planner then drains, so a folder whose layout is not recognisable costs 30 files, not a traversal.

Parameters:

path – dropped directory to inspect for a recognised image layout.

Returns:

{"labels": (...), "rows": [...], "error": ""}. Empty labels means no folder layout was recognised and there is nothing to report.

spacr.qt.dnd_handlers.scan_is_busy(screen) → bool[source]

True while a dropped folder is still being walked for screen.

Parameters:

screen – screen that owns the drop scanner to query.

spacr.qt.dnd_handlers.scan_mask_container(path) → Dict[str, Any][source]

Read a dropped container’s header and plan its extraction. Worker-safe.

The single-file half of a mask drop: .nd2 / .czi / .lif / multi-page .tif / .npz, described once and turned into the rows the metadata table shows. No Qt, no widgets, plain data out.

It is here rather than inside apply because describing a container is a FILE OPEN, and the drop that started this exercise was a file open on a sleeping autofs share that had not returned after twenty seconds. The branch used to defer itself with QTimer.singleShot(0, ...) under the comment “reads a header, not a tree” – true about the amount of data and beside the point about the wait, because a single-shot timer runs its callback ON the GUI thread with the event loop stopped. It only moved the freeze one turn later, which is why it was never traced back to here.

Parameters:

path – the dropped container file.

Returns:

{"found": bool, "summary": str, "rows": [...], "error": str}. found is False for a file no backend recognises.

Raises:

whatever spacr.qt.multi_format.describe_file() raises. A caller on a worker is wrapped by _scan_then(); the synchronous caller, _report_regex_on_mask(), has always let it through.

spacr.qt.dnd_handlers.scan_mask_drop(path) → Dict[str, Any][source]

Everything a mask drop asks the filesystem about one path. Worker-safe.

One record, taken once: whether the path is a folder or a file, whether the module can take it, and – when it cannot – the nearby folders the “did you mean” dialog offers.

It is one record because it used to be three separate rounds of stat-ing on the GUI thread inside a single drop: can_accept asked is_dir/has_images_in, suggest_alternatives then walked the parent and the children again, and apply asked is_file/is_dir a third time. On a sleeping /nas_mnt share the first of those had not returned after twenty seconds.

Parameters:

path – the dropped path to classify.

Returns:

{"is_dir": bool, "is_file": bool, "accepted": bool, "alternatives": [Path, ...]}.

spacr.qt.dnd_handlers.scan_mask_folder(path, sample: int = 20) → Dict[str, Any][source]

List the top level of a dropped folder once. Worker-safe.

Returns the filenames the regex preview samples plus the total image count the report quotes. Both used to come from two separate listings of the same directory, taken on the GUI thread.

Parameters:

path – dropped directory whose top-level files are inspected.

spacr.qt.dnd_handlers.table_names(path: pathlib.Path) → List[str][source]

Return the tables in a SQLite file, or [] for anything else.

One sqlite_master query; the table screens make the same one when they load. Kept here so the drop can ask which table rather than let load_path take the first one silently.

Opened read-only, and through a quoted URI: a folder with a ? or a # in its name would otherwise have everything after it read as query parameters, and the open would fail on a database that is perfectly fine.

Parameters:

path – candidate SQLite database path to inspect read-only.

Nested helpers

_decide.run() → None

Ask work on a throwaway thread and cache what it answers.

Outlives the wait below whenever the share is asleep, which is why _remember() refuses an answer older than the one it holds.

spacr/qt/dnd_handlers.py:573

_open_metadata_table._on_apply(csv_path)

Note where the metadata map was written.

spacr/qt/dnd_handlers.py:1146

_scan_then.guarded()

Run fn on the worker, carrying a failure back as data.

spacr/qt/dnd_handlers.py:323

_scan_then.landed(outcome) → None

Route one finished scan to on_done or on_error.

spacr/qt/dnd_handlers.py:331