spacr.qt.crop_thumbs¶
Decode object crops lazily, and keep the last few — the hover seam.
Showing the crop under the cursor is the difference between a scatter plot of ninety thousand dots and a scatter plot you can interrogate. It is also the easiest way to make a plot unusable: a PNG decode is a handful of milliseconds, a mouse sweep is a hundred hover events a second, and the two multiply into a plot that lags a quarter-second behind the cursor and feels broken.
So nothing here decodes on the paint path, and nothing decodes twice:
CropThumbnails.peek()answers from memory or not at all. It is what a hover handler calls, because the answer has to be instant or absent.CropThumbnails.pixmap()decodes on a miss. A caller runs it behind a short debounce (or on a click), never once per mouse-move event.The cache is keyed on
(path, mtime, size, px), followingspacr.crops, so a crop re-written by a re-run is re-read rather than served stale from the last session’s decode.
Why not QPixmap(path)¶
Crop PNGs are not ordinary images. Anything spaCR wrote before the BGR fix has
the first stain in the blue channel, and a 16-bit single-channel crop opened
with convert('RGB') is clipped to solid white by PIL. Both are corrected by
spacr.qt.annotate_engine.load_crop_image(), which is therefore the only
door used here — a hover preview showing different colours from the annotation
grid is worse than no hover preview, because both look plausible.
Classes¶
A bounded LRU of decoded crop thumbnails. |
Functions¶
|
|
A snapshot of the decoded-thumbnail caches that still have owners. |
|
|
The crop's path on this machine, re-anchored if the dataset moved. |
Module Contents¶
- class spacr.qt.crop_thumbs.CropThumbnails(db_path: str = '', *, size: int = DEFAULT_SIZE, capacity: int = DEFAULT_CAPACITY)[source]¶
A bounded LRU of decoded crop thumbnails.
- Parameters:
db_path – the
measurements.dbthe crops belong to. Used to re-anchor moved paths and to resolve the folder’s stored channel order; optional, because a folder of crops is a legitimate source too.size – longest edge of the thumbnails, in pixels.
capacity – how many to keep.
Not a
QObject, and holds no widget: a screen can own one, a test can drive one, and neither keeps the other alive.Create the thumbnail cache.
- Parameters:
db_path – measurements database the crops are read from.
size – longest edge of a decoded thumbnail, in pixels.
capacity – how many thumbnails to keep; the least recently used are dropped first.
- __contains__(path: object) bool[source]¶
Report whether a crop path is cached.
- Parameters:
path – the crop path; coerced with
str().- Returns:
Trueif it has an entry, including a cached failure.
- cache_budget_entries()[source]¶
(key, bytes, last use, in use)rows for the global sweep.A QLabel/QGraphicsItem that is displaying a pixmap owns its own implicitly-shared Qt value. Removing this lookup entry cannot blank that control, so no thumbnail entry needs pinning here.
- drop_cache_budget_entry(key) bool[source]¶
Evict one decoded thumbnail selected by the memory policy.
- Parameters:
key – cache key of the entry to evict, an
(abspath, mtime_ns, size, px)tuple; an unknown key is ignored andFalseis returned.
- peek(path: str) PySide6.QtGui.QPixmap | None[source]¶
The thumbnail if it is already decoded, else
None. Never blocks.The call a mouse-move handler makes. Returning
Nonemeans “not yet”, not “there is no crop” — askpixmap()for that, off the hover path.- Parameters:
path – path of the crop image; an empty value returns
None.
- pixmap(path: str) PySide6.QtGui.QPixmap | None[source]¶
The thumbnail, decoding it if this is the first time.
- Parameters:
path – path of the crop image to decode; an empty value returns
None.- Returns:
the
QPixmap, orNonewhen the crop cannot be read. A failure is cached asNonerather than raised: a missing file under the cursor must not throw out of a mouse handler, and it must not be retried sixty times a second either.
- prime(path: str) PySide6.QtGui.QPixmap | None[source]¶
Decode
pathnow so a laterpeek()is instant.The same as
pixmap(); named separately because the intent at the call site is different — this is what a debounce timer runs, and reading it as “prime the cache” rather than “get the pixmap” is what stops it drifting back onto the hover path.- Parameters:
path – path of the crop image to decode and cache, as for
pixmap().
- spacr.qt.crop_thumbs.crop_paths_for_keys(db_path: str, keys) Dict[str, str][source]¶
{object key: crop path}, resolved ONCE for a whole plot.spacr.active_learning.crops_for_object_keys()scans the crop table per call — right for opening a subset, ruinous once per hover event. This is the plot-time call: resolve every plotted point up front and let hover be a dict lookup.That function keeps the caller’s order but drops keys it cannot resolve, so when everything resolves the answer is a zip and costs one scan. When some keys miss, the returned list is a subsequence and no longer lines up, so the range is bisected: each half is asked separately until a half is either fully resolved (zip it) or a single key (resolve it or record the miss). That is a handful of extra scans for a handful of missing crops, rather than one scan per key.
Keys with no crop are absent from the result rather than mapped to
""— “this object has no crop” and “this object’s crop is at nowhere” are different claims and only the first one is true.
- spacr.qt.crop_thumbs.live_thumbnail_caches()[source]¶
A snapshot of the decoded-thumbnail caches that still have owners.
- spacr.qt.crop_thumbs.resolve_crop_path(path: str, db_path: str = '') str[source]¶
The crop’s path on this machine, re-anchored if the dataset moved.
png_liststores absolute paths built at measure time, so a dataset copied to another disk resolves to nothing and every preview is blank.spacr.qt.screens.annotate._reanchor_png_path()already knows how to rebuild one from the/data/segment under the database’s own root; this borrows it rather than growing a third copy of that rule, and falls back to the stored path when the Annotate screen is not importable (a headless test, a trimmed install).- Parameters:
path – crop path as stored in
png_list; returned unchanged when it is empty or already exists as a file.