spacr.qt.screens.convert

Format Converter — vendor microscopy files into Yokogawa TIFFs, with the mapping on screen before anything is written.

The screen exists because the conversion step is where a screen silently goes wrong. Rename 384 wells’ worth of ND2 into plate1_A01_T0001F001L01A01Z01C01.tif and the filenames stop carrying any trace of where they came from; get the well assignment wrong and nobody finds out until the hit list is being followed up, weeks later. So this screen does two things spacr.io.convert_to_yokogawa() never did: it shows the source → target table before writing, and it emits a map file that turns every converted name back into the original one.

Layout:

┌───────────────────────────────────────────────────────────────────┐
│ /data/run1                                    [Choose source…]    │
│ Layout [auto ▾]  Z [keep every plane ▾]  Plate names [plate1 ▾]   │
│ /data/run1_yokogawa                      [Choose destination…]    │
│                                     [Preview]        [Convert]    │
├───────────────────────────────────────────────────────────────────┤
│ source              target                       plate well  fld  │
│ run1/wt/f01_C1.tif  plate1_A01_T0001F001…C01.tif plate1 A01  1    │
│ run1/wt/f01_C2.tif  plate1_A01_T0001F001…C02.tif plate1 A01  1    │
│ …                                                                 │
├───────────────────────────────────────────────────────────────────┤
│ 20 file(s) would be written from 20 source(s).                    │
│ 1 plate(s), 1 well(s), 2 channel id(s).                           │
├───────────────────────────────────────────────────────────────────┤
│ Previewed 20 output file(s). Nothing has been written.            │
└───────────────────────────────────────────────────────────────────┘

Design notes:

  • The preview is the product. spacr.convert.scan() and spacr.convert.plan() write nothing at all; Convert is a separate press. A plan with a blocking error (two sources colliding on one output name) leaves Convert disabled — the fix is upstream, in the folder layout, not in a “yes, overwrite” button.

  • Everything heavy is in spacr.convert, which imports neither torch nor cellpose, so this stays a view and the logic is testable headless.

  • Off the GUI thread. Scanning a plate’s worth of ND2 headers takes seconds; converting takes minutes. Both go through spacr.qt.bridge.make_thread(), and the completion handler is reached through a bound method (ConvertScreen._job_settled) rather than a closure, because PipelineWorker.finished is emitted in the worker thread and a closure connected to it would build widget children there. Tests pass threaded=False.

  • No modal dialogs on any error path. A missing folder, an absent nd2reader, a name collision — all of it lands in the inline status label and the summary pane. A QMessageBox would hang a headless run.

Classes

ConvertScreen

Pick a source tree, review the mapping, convert, read the summary.

PlanTableModel

Read-only table model over a ConversionPlan.to_frame() frame.

Module Contents

class spacr.qt.screens.convert.ConvertScreen(parent=None, threaded: bool = True)[source]

Bases: PySide6.QtWidgets.QWidget

Pick a source tree, review the mapping, convert, read the summary.

Parameters:
  • parent – parent widget.

  • threaded – when False every job runs inline on the calling thread. Tests use it so assertions are exact; the app leaves it True so a 40-minute conversion does not freeze the window.

Build the screen and arm its drop zone.

Parameters:
  • parent – parent widget, or None.

  • threaded – preview and convert on a worker thread. Set False in tests so preview finishes before it returns.

active_jobs() → int[source]

How many worker threads are still winding down.

can_convert() → bool[source]

True when the Convert button is live.

destination_path() → str[source]

The destination folder currently typed in.

is_busy() → bool[source]

True while a scan or conversion is in flight.

layout_mode() → str[source]

The selected source layout.

plan() → spacr.convert.ConversionPlan | None[source]

The plan currently on screen, or None.

plate_naming() → str[source]

The selected plate naming scheme.

preview() → bool[source]

Scan the source and build the plan. Writes nothing.

Returns:

True when the scan was started (or, unthreaded, completed) — False when the source is unusable, with the reason in the inline status label.

preview_row_count() → int[source]

Rows in the preview table.

preview_targets() → List[str][source]

Every target filename in the preview, in table order.

preview_value(row: int, column: str) → str[source]

One preview cell by column name (test/introspection helper).

Parameters:
  • row – zero-based row of the preview table.

  • column – column name in the plan frame, e.g. "target"; an unknown column or out-of-range row gives "".

result() → spacr.convert.ConversionResult | None[source]

The result of the last conversion, or None.

resume_enabled() → bool[source]

Whether the next conversion will resume complete fields.

run_convert() → bool[source]

Convert the previewed plan into the destination folder.

Returns:

True when the job was started, False when it was refused — with the reason inline.

set_destination(path: str) → None[source]

Set the destination folder.

Parameters:

path – the output folder; None or empty clears the field.

set_layout_mode(value: str) → None[source]

Choose the source layout (see LAYOUT_CHOICES).

Parameters:

value – "auto", "plate_well", "well" or "flat"; any other value raises ValueError.

set_plate_naming(value: str) → None[source]

Choose how output plates are named.

Parameters:

value – "index" (plate1, plate2, …) or "name" (keep the folder name); any other value raises ValueError.

set_resume(enabled: bool) → None[source]

Enable or disable field-checkpoint resume.

Parameters:

enabled – True to switch the Resume toggle on.

set_source(path: str) → None[source]

Point the screen at a source folder without opening a dialog.

Parameters:

path – the source folder; when no destination is set yet, the destination becomes a sibling folder named after it with a _yokogawa suffix.

set_z_handling(value: str) → None[source]

Choose how z planes are treated (see Z_CHOICES).

Parameters:

value – "keep" (every plane), "max" (max-project) or "first" (first plane only); any other value raises ValueError.

source_path() → str[source]

The source folder currently typed in.

status_text() → str[source]

Current inline status message (test/introspection helper).

summary_text() → str[source]

Whatever is in the summary pane.

z_handling() → str[source]

The selected z handling.

class spacr.qt.screens.convert.PlanTableModel(parent=None)[source]

Bases: PySide6.QtCore.QAbstractTableModel

Read-only table model over a ConversionPlan.to_frame() frame.

A model rather than a QTableWidget because the preview for a full plate is tens of thousands of rows and populating that many QTableWidgetItems freezes the window for seconds.

Parameters:

parent – parent widget.

Create an empty conversion-plan model.

Parameters:

parent – parent object, or None.

columnCount(parent=QModelIndex()) → int[source]

How many columns the plan shows.

Parameters:

parent – unused; the model is flat.

Returns:

the column count.

data(index, role=Qt.DisplayRole)[source]

One cell of the plan.

Parameters:
  • index – the cell.

  • role – the Qt display role.

Returns:

the cell’s value for that role, or None.

frame() → pandas.DataFrame[source]

The frame currently displayed.

headerData(section, orientation, role=Qt.DisplayRole)[source]

One header label.

Parameters:
  • section – the row or column number.

  • orientation – which header.

  • role – the Qt display role.

Returns:

the label, or None.

rowCount(parent=QModelIndex()) → int[source]

How many files the conversion plan covers.

Parameters:

parent – unused; the model is flat.

Returns:

the row count.

set_frame(frame: pandas.DataFrame | None) → None[source]

Replace the displayed frame, keeping only the known columns.

Parameters:

frame – the plan frame from ConversionPlan.to_frame(); None or an empty frame shows an empty table with every preview column.

Nested helpers

ConvertScreen.preview._job()

Scan the source and plan the conversion. Off the GUI thread.

spacr/qt/screens/convert.py:672

ConvertScreen.run_convert._job()

Run the conversion. Off the GUI thread.

spacr/qt/screens/convert.py:776